The decorator in python is mainly used to append the information that needs to be output before the existing function implements the function. The following example will show how I write the decorator.
First try to write decorators to decorate a parameterless function and a parameterized function respectively
1 def my_log(func): 2 def wrapper(): 3 print ( ' decorator works ' ) 4 func() 5 return wrapper #returns just the wrapper function object, which is not running at this time 6 7 @my_log 8 def run() : # run=my_log(run)=wrapper returns the wrapper function object, not running 9 print ( ' run ' ) 10 11 run() # run()=my_log(run())=wrapper() At this time, call and Run the wrapper() function 12 13 #The results are as follows 14 # decorator works 15 # run
1 def my_log(func): 2 def wrapper(): 3 print ( ' decorator works ' ) 4 func() 5 return wrapper #returns just the wrapper function object, it does not run at this time 6 7 @my_log 8 def add(x ,y): # add=my_log(add)=wrapper also returns the wrapper function object, not running 9 print (x+ y) 10 11 add(3,6 ) 12 #Attempt to call and run wrapper() 13 14 #The result is an error 15 #TypeError: wrapper() takes 0 positional arguments but 2 were given 16 #The reason is that two parameters were passed in when running add(3,6) add(3,6)=my_log(add(3,6))=wrapper( 3,6) But no parameters are received in the wrapper() function
The correct decorator function after modification is
1 def my_log(func): 2 def wrapper(x,y): 3 print ( ' decorator works ' ) 4 func(x,y) 5 return wrapper #returns just the wrapper function object, not running at this time 6 7 @ my_log 8 def add(x,y): # add=my_log(add)=wrapper returns the wrapper function object, not running 9 print (x+ y) 10 11 add(3,6 ) 12 13 #The result of the operation is as follows 14 # decorator works 15 #9
But not a perfect decorator function
1 def my_log(func): 2 def wrapper(x,y): 3 print ( ' decorator works ' ) 4 func(x,y) 5 return wrapper #returns just the wrapper function object, not running at this time 6 7 @ my_log 8 def add(x,y): 9 print (x+ y) 10 11 add(3,6 ) 12 13 @my_log 14 def run(): 15 print ( ' run ' ) 16 17 run() 18 19 #But an error occurred when adding the parameterless function run() 20 # TypeError: wrapper() missing 2 required positional arguments: 'x' and 'y' 21 #Because run()=my_log(run ())=wrapper() is not passed to the two parameters required by the wrapper
At this time, it is necessary to modify the wrapper function in the decorator to be a non-keyword variable parameter and a keyword variable parameter to ensure normal operation regardless of whether there are parameters passed in
1 def my_log(func): 2 def wrapper(*args,** kwargs): 3 print ( ' decorator works ' ) 4 func(*args,** kwargs) 5 return wrapper #returns only the wrapper function object, at this time did not run 6 7 @my_log 8 def add(x,y): 9 print (x+ y) 10 11 add(3,6 ) 12 13 @my_log 14 def run(): 15 print ( ' run' ) 16 17 run() 18 19 #The result of running is as follows 20 # decorator works 21 # 9 22 # decorator works 23 # run
But the decorator function is not completely correct at this time
1 def my_log(func): 2 def wrapper(*args,** kwargs): 3 print ( ' decorator works ' ) 4 func(*args,** kwargs) 5 return wrapper #returns only the wrapper function object, at this time did not run 6 7 @my_log 8 def add(x,y): 9 print (x+ y) 10 11 @my_log 12 def run(): 13 print ( ' run ' ) 14 15 print (run. __name__ ) 16 print (add. __name__ ) 17 18 #The result of the operation is as follows 19 # wrapper 20 # wrapper 21 #Because of run=my_log(run)=wrapper, the __name__ attribute of the run method and the add method is secretly modified
You need to add @wraps inside the decorator function to ensure that the __name__ attribute of the decorated function is not modified
1 from functools import wraps 2 3 def my_log(func): 4 @wraps(func) 5 def wrapper(*args,** kwargs): 6 print ( ' decorator works ' ) 7 func(*args,** kwargs) 8 return wrapper #Only the wrapper function object is returned, and it is not running at this time 9 10 @my_log 11 def run(): # run=my_log(run)=wrapper returns the wrapper function object, not running 12 print ( ' running' ) 13 14 run() 15 # run()=my_log(run())=wrapper() 16 17 print ( " The __name__ of the function run is %s " %run. __name__ ) 18 print ( ' - ' * 30 ) 19 20 @my_log # add=my_log(add)=wrapper also returns the wrapper function object 21 def add(a,b): 22 print (a+ b) 23 24 add(3,4 ) 25 # add(3, 4)=my_log(add(3,4))=wrapper(3,4) 26 print ( " The __name__ of the function add is %s " %add. __name__ ) 27 28 #The running result is as follows 29 # running 30 # The __name__ of the function run is run 31 # ----------- ------------------- 32 # decorator works 33 # 7 34 # __name__ of function add is add
Final summary:
1. The function defined in the decorator should use the combination of *args and **kwargs to receive any parameters of the function that may be decorated. When executing the original function in the wrapper function in the decorator, you need to pass in *args and **kwargs
2. Use functools.wraps to wrap the wrapper function with @wraps before the wrapper function in the decorator to prevent the __name__ attribute of the decorated function from being modified