python Decorator

Keywords: Python

1, Decorator definition

The way to dynamically add functionality during code execution is called a Decorator. In essence, Decorator is a higher-order function of return function.

 1 >>> def log(func):
 2 ...     def wrapper(*args, **kw):
 3 ...         print('call %s:' % func.__name__)
 4 ...         return func(*args, **kw)
 5 ...     return wrapper
 6 ... 
 7 >>> @log
 8 ... def now():
 9 ...     print('2017-12-16')
10 ... 
11 >>> now()
12 call now:
13 2017-12-16
14 >>> 

Look at the log above, because it is a decorator, so take a function as an argument and return a function. To use Python's @ syntax, place the decorator at the definition of the function.

Putting @ log in the definition of the now() function is equivalent to executing the statement:

1 >>>now = log(now)

2, Decorator with parameters

 1 >>> def log(text):
 2 ...     def decorator(func):
 3 ...         def wrapper(*args, **kw):
 4 ...             print('%s %s:' % (text, func.__name__))
 5 ...             return func(*args, **kw)
 6 ...         return wrapper
 7 ...     return decorator
 8 ... 
 9 >>> @log('execute')
10 ... def now():
11 ...     print('2017-12-16')
12 ... 
13 >>> now()
14 execute now:
15 2017-12-16

Compared with two layers of nested decorator s, the effect of three layers of nesting is as follows:

1 >>> now = log('execute')(now)

3, functools.wraps

There is no problem in the definition of the above two decorators, but it is not the last step. Because we said that functions are also objects. They have properties such as ﹣ name ﹣ and so on. But when you look at functions decorated by decorator, their ﹣ name ﹣ has changed from 'now' to 'wrapper':

1 >>> now.__name__
2 'wrapper'

Because the name of the wrapper() function returned is' wrapper ', you need to copy the properties of the original function such as' name' to the wrapper() function. Otherwise, some code execution that depends on the signature of the function will fail.

There is no need to write code like wrapper. Name = func. Name. Python's built-in functools.wraps does this. Therefore, a complete decorator is written as follows:

1 import functools
2 
3 def log(func):
4     @functools.wraps(func)
5     def wrapper(*args, **kw):
6         print('call %s():' % func.__name__)
7         return func(*args, **kw)
8     return wrapper
 1 import functools
 2 
 3 def log(text):
 4     def decorator(func):
 5         @functools.wraps(func)
 6         def wrapper(*args, **kw):
 7             print('%s %s():' % (text, func.__name__))
 8             return func(*args, **kw)
 9         return wrapper
10     return decorator

Posted by cape on Wed, 13 May 2020 09:04:20 -0700