python Decorator has a very important and wide range of applications . stay python In language , The main purpose of decorator is to simplify the code , Avoid code block duplication . The introduction of decorator can expand the function , Make the function have extra function , Such as transaction processing , performance testing , Caching and so on . Here we give some examples 「 Calculate function execution time 」 This is an example .

Calculate the execution time of a function , You can use the following code :
def func1(): start = time() sleep(1) #do something end = time() print("total
execute time of {} is {}".format(func1.__name__, end - start)) func1()
#Out:total execute time of func1 is 1.002723217010498

so , Let a function implement 「 Calculate self execution time 」 We need three lines of code , Maybe the three lines are short , Just put it inside the function body . however , What if there are three functions that need to perform this function ? So the code is as follows :
def func1(): start = time() sleep(1) end = time() print("total execute time of
{} is {}".format(func1.__name__, end - start)) def func2(): start = time() sleep
(1) end = time() print("total execute time of {} is {}".format(func1.__name__,
end- start)) def func3(): start = time() sleep(1) end = time() print("total
execute time of {} is {}".format(func1.__name__, end - start)) func1() func2()
func3()

There are many redundant parts in the above code , Three functions 「 Calculate execution time 」 The code is the same . Imagine , If we have 1000 It takes time to compute a function , So what is the code for calculating time 3000 that 's ok , It's a little scary . therefore , We need to consider how to reduce the amount of code redundancy .

It's time for decorators to show off , Decorator can extend a function . The decorator itself is also a function , The type parameter passed to him is the function object to be extended , It also returns a function object . When extending a function with decorator , Only when this function is defined , Use keywords ( Grammar sugar )@ Declare its decorator name .

The 「 Calculate execution time 」 The function is transformed into the form of using decorator , The code is as follows :
def timer(func): # It's coming in here func It's the extended function def wrapper(): start = time() func()
# Where functions are executed , It's execution sleep(1) end = time() print("total execute time of {} is {}".format(
func.__name__, end - start)) return wrapper @timer def func1(): sleep(1) @timer
def func2(): sleep(1) @timer def func3(): sleep(1) func1() func2() func3()

It can be seen from the above code , Originally 3 Function time calculation of row , Become just 1 Yes @timer, This makes the code concise and reusable . If there is one now n A function that needs to call this function , The amount of code can be reduced 2(n-1) that 's ok .

It is worth noting that , stay python All things in the world are objects , Functions are also objects . In the first line of the decorator , function func Pass in the decorator as a function object , this func There are no brackets at the end , So it can be passed but not executed ; If the function name is followed by parentheses , The function executes .

The above examples do not require any parameters , But if there is no parameter passing , This causes the decorator to always perform fixed logic , Limited to some single scene . We introduce decorators to reduce the amount of code , So when the two logics are very close , If you use a decorator that doesn't transfer parameters , You may need to write two decorators to implement two similar logic . however , When the decorator can pass in parameters , One decorator can be used to implement two or more similar logic .

For example, the sleep time of each function is no longer fixed 1s, It's a variable ; Now we need to understand this sleep Time to make a judgment , Let's see what this function is 「 Juneng sleep 」 or 「 The giant can't sleep 」.

There are two ways to pass parameters : One is to pass parameters when a function is defined , At this point, the parameters need to be passed in the syntax @ In that line of business ; The second is to pass parameters when the function is executed , At this time, the parameter is passed in the place where the original function is called . Here is the code for the first type of parameter passing :
def judger(t): # Incoming t Is the parameter passed when the function is defined , Grammar sugar @ In that line of business def wrapper(func): if t > 5: print(
" Juneng sleep ") else: print(" The giant can't sleep ") func() return wrapper @judger(3) def func1(): sleep(3
) @judger(6) def func2(): sleep(6) #Out: # The giant can't sleep # Juneng sleep

In the above code , Our function extension function is to determine the function 「 Can I sleep 」, This is where we need to be 「 Sleep time 」 This parameter is passed into the decorator . It is different from the previous one without parameters , The name of the decorator definition judger You need to receive grammar sugar later @ The parameters in that line . When you define a function, you talk about it 3 and 6 Into the decorator .

The second method is to pass parameters during function execution , In order to eliminate the limitation of the number of parameters , Dynamic parameters are generally used *args, and **kwargs. among *args Is a tuple of parameters (tuple),**kwargs Is a dictionary that records keywords and their corresponding values . The sample code is as follows :
def adder(func): # It's coming in here func It's the extended function def wrapper(*args, **kwargs): total = 0
for item in args: total += item print(total) return wrapper @adder def func1(*
args, **kwargs): pass func1(3, 5, 7) #Out:15
Here I define the expanded function of decorator , Get the sum of all the parameters in the execution of the function . Because there is no parameter passing in the function definition , So there's no need to receive it on the first line of the decorator @ The parameters passed in that line .

It can be found that , Writing a decorator requires at most three nesting levels . In this case, the parameters are transferred when the existing function is defined , There are also arguments that are passed when the function is executed . This triple nesting is also relatively simple , Only on the basis of the second case above , Nest one more layer on the outside , Used to receive grammar sugar @ The parameters passed in that line . The general form of this ornament is as follows :
def judger(*args, **kwargs): # Parameters passed from function definition , Namely 3 def wrapper(func): print(args) def
dec(*args, **kwargs): # Parameters passed during function execution , Namely 2,4,5 """do something""" func(*args, **kwargs
) print(args) return dec return wrapper @judger(3) def func1(*args, **kwargs):
pass func1(2,4,5) #Out: (3,) (2, 4, 5)
This two places have parameter transfer form is the most complex , You only need to write three nesting .

Finally, there's a small one trick, For the above code , Generally, a module needs to be called to retain the meta information of the original function :
from functools import wraps def judger(*args, **kwargs): @wraps(func) def
wrapper(func): print(args) wraps(func) def dec(*args, **kwargs): """do
something""" func(*args, **kwargs) print(args) return dec return wrapper @judger
(3) def func1(*args, **kwargs): pass func1(2,4,5)

If you don't use this module , that func1 Will lose his meta information , as __name__,__doc__ wait , The result is that this information becomes a decorator function judger Corresponding information in . therefore , To make these attributes match our intuition better , It's better to add this module .

Technology