Describes how to use the Hook technique in Python development and the pythonhook technique.
What is a Hook is to add some hooks to an existing method so that some additional processing is performed before or after the method is executed, so what is the function of the Hook technique and why we need to use it? In fact, if a project has sufficient considerations in designing the architecture and the module abstraction is reasonable enough, at the beginning of the design, we reserved enough interfaces for future extensions, so we can skip the Hook technique. However, at the beginning of the project design, it is often impossible for the architects to think far-reaching enough, which makes Shenzhen face the pain of restructuring in the future expansion. At this time, the Hook technique seems to be able to bring us a piece of cake, add hooks to the old architecture to meet new expansion requirements.
Next, let's take a look at the Hook processing. We will introduce them one by one based on the level of the Hook object.
Hook a class
That is to say, we need to monitor the creation and other operations of the class, and then perform the operations we want before or after this.
1. Create a Hook class
You can add the _ metaclass _ attribute to a class when writing it.
class Foo(Bar): __metaclass__ = something…
The process of creating a class in Python is as follows:
Is the _ metaclass _ attribute in Foo? If yes, Python creates a class named Foo through _ metaclass _ in the memory. If Python does not find _ metaclass __, it will continue to search for the _ metaclass _ attribute in Bar (parent class) and try the same operations as the previous one. If Python cannot find _ metaclass __in any parent class, it searches for _ metaclass __at the module level and tries the same operation. If _ metaclass __is still not found, Python will use the built-in type to create this class object.
Therefore, the value of the _ metaclass _ attribute must be something that can create a class, that is, a class that inherits the type.
For example:
Copy codeThe Code is as follows:
Class Singleton (type): def _ init _ (cls, name, bases, dict): super (Singleton, cls ). _ init _ (name, bases, dict) cls. _ instance = None def _ call _ (cls, * args, ** kw): if cls. _ instance is None: cls. _ instance = super (Singleton, cls ). _ call _ (* args, ** kw) return cls. _ instanceclass MyClass (object): _ metaclass _ = Singleton
Singleton is an object that can create classes because it inherits the type
For this reason, we can monitor the creation process of MyClass In the Singleton class.
2. Hook instance attributes
Here we need to operate on the properties of _ getattribute _ and _ getattr __
Object. _ getattribute _ (self, name): This method is accessed first regardless of whether the access exists or does not exist.
Object. _ getattr _ (self, name): Access this method when the _ getattribute _ method does not exist or an AttributeError error occurs.
Copy codeThe Code is as follows:
Class C (object): a = 'abc' def _ getattribute _ (self, * args, ** kwargs): print (_ getattribute _ () is called) return object. _ getattribute _ (self, * args, ** kwargs) def _ getattr _ (self, name): print (_ getattr _ () is called) return namec = C () print c. a _ getattribute _ () is calledabcprint c. aa _ getattribute _ () is called _ getattr _ () is calledaa
We can see that when accessing an existing attribute a, __getattribute _ is called. when accessing the undefined attribute aa, _ getattribute _ is called first, and _ getattr _ is called.
3. Hook attributes
The python descriptor is an object attribute of "binding behavior". In the descriptor protocol, it can override the access to the attribute through methods. These methods include _ get _ (), _ set _ (), and _ delete __(). If any of these methods is defined in an object, this object is a descriptor.
Copy codeThe Code is as follows:
Class Desc (object): def _ get _ (self, instance, owner): print (_ get __...) def _ set _ (self, instance, value): print ('_ set __... ') class TestDesc (object): x = Desc () t = TestDesc () t. x _ get __...
-Self: Desc instance object, which is actually the property of TestDesc x
-Instance: instance Object of TestDesc, which is actually t
-Owner: who owns these things. Of course, it is the class TestDesc. It is the highest ruler, and others are included in or generated by it.
To make descriptors work properly, they must be defined at the class level. Otherwise, Python cannot automatically call the _ get _ and _ set _ methods for you.
According to the previous descriptions of class methods, will the TestDesc _ getattribute _ method be referenced when t. x is referenced? The answer is yes. In fact, the actual search order in python is as follows:
1) _ getattribute _ (), call unconditionally
2) Data Descriptor (defined by _ set _ or _ delete _): from 1) trigger call (if the _ getattribute _ () method is reloaded manually, the descriptor may not be called)
3) instance object dictionary
4) Class dictionary
5) non-data Descriptor (only the _ get _ descriptor is defined)
6) Dictionary of the parent class
7) _ getattr _ () method
4. Hook classes with Modifiers
Copy codeThe Code is as follows:
Def singleton (cls, * args, ** kw): instances = {} def _ singleton (): if cls not in instances: instances [cls] = cls (* args, ** kw) return instances [cls] return _ singleton @ singletonclass MyClass (object): a = 1 def _ init _ (self, x = 0): self. x = x
We use the singleton method to modify MyClass to a singleton mode, and we also monitor the process of MyClass instances in the singleton method.
Hook Methods
1. Hook methods with Modifiers
1) modify the method without Parameters
Copy codeThe Code is as follows:
Def something (func): def wrap (): print start func () print end return wrap @ somethingdef func (): pass
2) modify the method with Parameters
Copy codeThe Code is as follows:
Def something (func): defwrap (* args, ** kargv): print startfunc (* args, ** kargv) print end return wrap @ somethingdef func (a, B): pass
3) Use a modifier with parameters to modify the method.
Copy codeThe Code is as follows:
Def something (a, B): def new_func (func): def wrap (* args, ** kargv): print a func (* args, ** kargv) print breturn wrap return new_func @ something (1, 2) def func (a, B): pass
Other hooks
1. built-in Hook Methods
Copy codeThe Code is as follows:
# Hookopen method real_open = _ builtins __. open _ builtin __. open = my_open # Hookimport method real_importer = _ import ____ builtins __. _ import _ = my_importer
The above operations make my_open Replace the python built-in open method, so we can use our own my_open method to monitor subsequent calls to the open method.
2. Monkey Patch
Copy codeThe Code is as follows:
From SomeOtherProduct. SomeModule import SomeClassdef speak (self): return "ookookeeeeeeeee! "SomeClass. speak = speak
In fact, this is the Hook technique used in all languages. We often use third-party packages and want to make some extensions on them, but do not want to use them when changing the original code.
More
The above mentioned modifier operations, so we have some tips to understand when using modifiers.
1. Use functools
Prevent function signature changes after modifier is used
Copy codeThe Code is as follows: from functools import wrapsdef my_dec (func): @ wraps (func) def wrapped (): print % siscalled % func. _ name _ return func () return wrapped @ my_decdef foo (): pass
After such processing, the signature of the foo method is consistent with that before modification. Otherwise, the signature will become the signature of the my_dec method.
2. Use the decorator module as the Modifier
Copy codeThe Code is as follows:
From decorator import decorator @ decoratordef wrap (f, * args, ** kw): print start f (* args, ** kw) print end # in this way, the wrap method becomes a decorator @ wrapdef func (): print func
3. Use classes as modifiers
Copy codeThe Code is as follows: class test (object): def _ init _ (self, func): self. _ func = func def _ call _ (self): print start self. _ func () print end @ testdef func (): print funcfunc () startfuncend
In practice, a class is rarely used as the modifier, but in fact, as long as a class implements the _ call _ method, it can be used as a modifier, in addition, because the class is more operable than the method, the class modifier can also implement richer features.
The following is an example for further understanding.
#-*-Coding: UTF-8-*-# import pythoncom import pyHook def onMouseEvent (event): # Listen to mouse event print "MessageName:", event. messageName print "Message:", event. message print "Time:", event. time print "Window:", event. window print "WindowName:", event. windowName print "Position:", event. position print "Wheel:", event. wheel print "Injected:", event. injected print "---" # Return True to pass the event to another handler # Note: if False is returned here, the mouse event is intercepted. # That is to say, your mouse seems to be frozen. It seems that you have lost the response. return Truedef onKeyboardEvent (event): # Listen to the keyboard event print "MessageName :", event. messageName print "Message:", event. message print "Time:", event. time print "Window:", event. window print "WindowName:", event. windowName print "Ascii:", event. ascii, chr (event. ascii) print "Key:", event. key print "KeyID:", event. keyID print "ScanCode:", event. scanCode print "Extended:", event. extended print "Injected:", event. injected print "Alt", event. alt print "Transition", event. transition print "---" # return True def main (): # create a "Hook" management object hm = pyHook. hookManager () # Listen to all Keyboard Events hm. keyDown = onKeyboardEvent # Set the keyboard "hook" hm. hookKeyboard () # Listen to all mouse events hm. mouseAll = onMouseEvent # Set the mouse "Hook" hm. hookMouse () # enters the loop. If you do not close it manually, the program will remain in the listening status pythoncom. pumpMessages () if _ name _ = "_ main _": main ()
Export test.pyto test.exe # Get py2exe from http://www.py2exe.org/from distutils. core import setupimport py2exesetup (console = ['test. py ']) # Run python setup in cmd. py py2exe, which has exe and required dll under the dist directory
# Hide the console and let it flash through import ctypes whnd = ctypes. windll. kernel32.GetConsoleWindow () if whnd! = 0: ctypes. windll. user32.ShowWindow (whnd, 0) ctypes. windll. kernel32.CloseHandle (whnd)
Let me talk about it first. The content we are talking about today is hard knowledge, which may not be available in the general development process. However, understanding this knowledge is very helpful for improving programming capabilities, it also helps you better understand the Python mechanism. We also hope that you can support the customer's home.