Writing decorators in python

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

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324487448&siteId=291194637