In Python, functions can be passed into a function as parameters, and functions can also be copied to variables to call functions through variables. The decorator can extend the function of a function and make a decorator annotation for the function. The functions defined in the decorator can be executed in advance for all functions to improve the reuse of code.
So now there is such a scene.
punch the clock
There are various positions in Internet companies, such as programmer, front desk, Programmers need to punch in before opening the computer. The front desk should open the door early (in fact, I don't know who opens the door. It is assumed that the front desk opens the door first). The front desk also needs to punch in before opening the door. In other words, punch in is the first public action of all employees, so the function of punch in can be taken out as public logic.
Ordinary function call method
Naturally, it can be realized as follows.
def di(f): print('%s punch the clock,drop...' % f.__name__) return f() def boot(): print('Power on') def open(): print('Open the door') if __name__ == '__main__': """ Programmers need to punch in the fingerprint machine outside the door before starting up and before the front desk opens the door. """ di(boot) di(open)
A function di(f) is defined to print F__ name__ That is, the function name information of F, and return the execution result of f().
Note:__ name__ If you import as a module, module__ name__ Is the name of the module itself. If the module executes itself as a script, it returns__ main__.
Execution results:
boot punch the clock,drop... Power on open punch the clock,drop... Open the door
In this design, if there are many functions to call, it will be very troublesome, so the decorator will come in handy.
Simple decorator and @ grammar sugar
Decorator: a way to dynamically add functionality during code execution, called a decorator.
Simple decorator
Define a di(f) method, or pass in the function of the logic to be executed as a parameter, which defines a wrapper function, and the return value is the execution result of F. In if__ name__ == '__ main__': Inside, the decorator is called, the defined function is not modified, and the function "punch in" is dynamically added during operation.
import functools # Simple decorator def di(f): """ Programmers need to punch in the fingerprint machine outside the door before starting up and before the front desk opens the door. :param f: Pass in a function :return: """ # Put the original function__ name__ Copy properties such as to wrapper() @functools.wraps(f) def wrapper(): print('%s punch the clock,drop...' % f.__name__) return f() return wrapper def boot(): print('Power on') def open(): print('Open the door') if __name__ == '__main__': # First, a simple decorator a = di(boot) a1 = di(open) print(a.__name__) # Result wrapper plus @ functools After wraps (f), the result is boot a() a1()
The return value a of di(boot) is the wrapper function. The wrapper function is called through a() to get the return value of boot. Similarly, di(open) is the same.
result
boot boot punch the clock,drop... Power on open punch the clock,drop... Open the door
Since the return value a of di(boot) is the wrapper function, print (A. _name_) Of course, the result is wrapper. We hope it is boot. What should we do? Functools Wraps (f) this annotation can take the original function boot__ name__ Copy the attributes to wrapper(), and the comments on this line of code can also be run, then print (A. _name_) The result is the wrapper.
Second, @ grammar sugar
Through @ syntax sugar, decorators can also be applied to functions. It is recommended.
import functools def di(f): """ Programmers need to punch in the fingerprint machine outside the door before starting up and before the front desk opens the door. :param f: Pass in a function :return: """ # Put the original function__ name__ Copy properties such as to wrapper() @functools.wraps(f) def wrapper(): print('%s punch the clock,drop...' % f.__name__) return f() return wrapper # @Grammar sugar @di def boot2(): print('Power on') @di def open2(): print('Open the door') if __name__ == '__main__': # Second, @ grammar sugar boot2() open2()
@The di tag is equivalent to, a2 = di(boot2) a2(). It doesn't need to be so troublesome because it is marked with the @ symbol. Just call the decorator with boot2().
result
boot2 punch the clock,drop... Power on open2 punch the clock,drop... Open the door
Business logic function requires parameters
Business logic functions may require parameters, such as:
def boot(name): print('%s Power on' % name)
Then, you only need to modify the front decorator to:
import functools # Business logic function requires parameters def di(f): """ Programmers need to punch in the fingerprint machine outside the door before starting up and before the front desk opens the door. :param f: Pass in a function :return: """ # Put the original function__ name__ Copy properties such as to wrapper() @functools.wraps(f) def wrapper(*args, **kwargs): print('%s punch the clock,drop...' % f.__name__) return f(*args, **kwargs) return wrapper @di def boot(name): print('%s Power on' % name) if __name__ == '__main__': boot('keguang')
result:
boot punch the clock,drop... keguang Power on
Add * args, **kwargs parameters to the wrapper, and directly call f(*args, **kwargs) in boot. By the way:
- *args: an array parameter can be passed in
- **kwargs: you can pass in a k-v pair parameter
The sequence is corresponding, and the array parameter comes first. give an example:
def f(*args, **kwargs): print('args=', args) print('kwargs=', kwargs) print(f(1, 2, 3, a = 'a', b = 'b')) # result # args= (1, 2, 3) # kwargs= {'a': 'a', 'b': 'b'}
Decorator with parameters
If the decorator also has parameters, for example, if an employee comes to work early in the morning & lt; 9: 00, I can give a compliment, so it's equivalent to just setting a layer of function outside the previous di(), di_args, in the wrapper. Use this parameter
import functools # Decorator with parameters def di_args(time): def di(f): """ Programmers need to punch in the fingerprint machine outside the door before starting up and before the front desk opens the door. :param f: Pass in a function :return: """ # Put the original function__ name__ Copy properties such as to wrapper() @functools.wraps(f) def wrapper(*args, **kwargs): if time < '9:00': print('It's so early. It's great...') print('%s punch the clock,drop...' % f.__name__) return f(*args, **kwargs) return wrapper return di @di_args('8:00') def boot(name): print('%s Power on' % name) if __name__ == '__main__': boot('keguang')
Parameter at @ di_args('8:00 ') can be passed in, which is a bit like the annotation in java. Finally, you can call it through boot('keguang '). The result is as follows:
It's so early. It's great... boot punch the clock,drop... keguang Power on
Class decorator
Class decorators mainly rely on class__ call__ Method, which is called when the decorator is attached to a function using the @ form.
# Class decorator class di(object): def __init__(self, f): self._f = f def __call__(self, *args, **kwargs): print('decorator start...') self._f() print('decorator end...') @di def boot(): print('Power on') if __name__ == '__main__': boot()
With the @ Di decorator ID, the di class will be instantiated with boot, and then executed__ call__ Function, object indicates that this class can pass in any type of parameter. Operation results
decorator start... Power on decorator end...
A typical application scenario of decorator is to log. If all logic needs to log the running status of the program, you can add log module decorator to these logic (functions) to achieve the corresponding purpose.
last
If the article is useful to you, praise the author
Refuse white whoring, start with me