Platitude Python advanced decorator
- 2020-06-01 10:09:10
- OfStack
Functions are objects
To understand the Python decorator, it is important to understand that in Python, a function is also an object, so you can think of the function name when you define the function as a reference to the function object. Since it is a reference, you can assign a function to a variable, or you can pass or return the function as a parameter. At the same time, functions can be redefined in the body.
Nature of decorator
You can restore what the decorator wants to do by writing an example of a pure function.
def decorator(func):
def wrap():
print("Doing someting before executing func()")
func()
print("Doing someting after executing func()")
return wrap
def fun_test():
print("func")
fun_test = decorator(fun_test)
fun_test()
# Output:
# Doing someting before executing func()
# func
# Doing someting after executing func()
A reference to the function pointed to by fun_test is passed to the decorator() function
The decorator() function defines the wrap() subfunction, which calls the fun_test() function passed in by the func reference, and does something else before and after calling the function
The decorator() function returns a reference to the internally defined wrap() function
fun_test receives the function reference returned by decorator(), thus pointing to a new function object
The front and back decoration of the fun_test() function is completed by calling the new function fun_test() to perform the functions of the wrap() function
Decorators are used in Python
The decorator feature can be easily used in Python via the @ symbol.
def decorator(func):
def wrap():
print("Doing someting before executing func()")
func()
print("Doing someting after executing func()")
return wrap
@decorator
def fun_test():
print("func")
fun_test()
# Output:
# Doing someting before executing func()
# func
# Doing someting after executing func()
The decorator function is already implemented, but at this point:
print(fun_test.__name__)
# Output:
# wrap
S 52en_test. s 54en__ has become s 55en, this is because s 56en () function has already rewritten the name and annotation document of our function. This problem can be solved by functools.wraps at this point. wraps takes a function to decorate and adds the ability to copy function names, comment documents, parameter lists, and so on. This allows us to access the properties of the function before the decoration in the decorator.
A more formal way of writing:
from functools import wraps
def decorator(func):
@wraps(func)
def wrap():
print("Doing someting before executing func()")
func()
print("Doing someting after executing func()")
return wrap
@decorator
def fun_test():
print("func")
fun_test()
print(fun_test.__name__)
# Output:
# Doing someting before executing func()
# func
# Doing someting after executing func()
# fun_test
Parameter decorator
By returning a wrapper function, you can simulate the wraps decorator and construct a decorator with parameters.
from functools import wraps
def loginfo(info='info1'):
def loginfo_decorator(func):
@wraps(func)
def wrap_func(*args, **kwargs):
print(func.__name__ + ' was called')
print('info: %s' % info)
return func(*args, **kwargs)
return wrap_func
return loginfo_decorator
@loginfo()
def func1():
pass
func1()
# Output:
# func1 was called
# info: info1
@loginfo(info='info2')
def func2():
pass
func2()
# Output:
# func2 was called
# info: info2
A decorator classes
You can also implement decorators by writing classes, and make them available for inheritance and other object-oriented features that are more practical
First, write a decorator base class:
from functools import wraps
class loginfo:
def __init__(self, info='info1'):
self.info = info
def __call__(self, func):
@wrap
def wrap_func(*args, **kwargs):
print(func.__name__ + ' was called')
print('info: %s' % self.info)
self.after() # call after Method, which can be implemented in subclasses
return func(*args, **kwargs)
return wrap_func
def after(self):
pass
@loginfo(info='info2')
def func1():
pass
# Output:
# func1 was called
# info: info1
Extend the functionality of the decorator by inheriting the loginfo class:
class loginfo_after(loginfo):
def __init__(self, info2='info2', *args, **kwargs):
self.info2 = info2
super(loginfo_after, self).__init__(*args, **kwargs)
def after(self):
print('after: %s' % self.info2)
@loginfo_after()
def func2():
pass
func2()
# Output:
# func2 was called
# info: info1
# after: info2