python decorator is a function used to expand the function of the original function. The purpose is to add new functions to the function without changing the original function name (or class name).
The special feature of this function is that its return value is also a function, which is a function embedded with the "original" function.
Generally speaking, if we want to expand the original function code, the most direct way is to invade the code and modify it, for example:
import time def f(): print("hello") time.sleep(1) print("world")
This is our original function, and then we try to record the total execution time of this function. The simplest way is to change the original code.
import time def f(): start_time = time.time() print("hello") time.sleep(1) print("world") end_time = time.time() execution_time = (end_time - start_time)*1000 print("time is %d ms" %execution_time)
But in practice, sometimes the core code can not be changed directly, so without changing the original code, we can define a function. (But to take effect, you need to execute the function again)
import time def deco(func): start_time = time.time() f() end_time = time.time() execution_time = (end_time - start_time)*1000 print("time is %d ms" %execution_time) def f(): print("hello") time.sleep(1) print("world") if __name__ == '__main__': deco(f) print("f.__name__ is",f.__name__) print()
Here we define a function deco, whose parameter is a function, and then embeds the timing function into the function. But we want to expand these ten million functions.
That's to execute deco() 10 million times, so that's not ideal! Next, we can try to use decorators to achieve, first look at the original appearance of decorators.
import time def deco(f): def wrapper(): start_time = time.time() f() end_time = time.time() execution_time = (end_time - start_time)*1000 print("time is %d ms" %execution_time ) return wrapper @deco def f(): print("hello") time.sleep(1) print("world") if __name__ == '__main__': f()
The deco function here is the most primitive decorator. Its parameter is a function, and the return value is also a function.
The function f() as a parameter is executed inside the return function wrapper(). Then before function f(), add @deco.
The f() function is equivalent to being injected with the timing function. Now, if only f() is called, it will become a function with more new functions.
There is no need to repeat the original function.
Extension 1: Decorators with Fixed Parameters
import time def deco(f): def wrapper(a,b): start_time = time.time() f(a,b) end_time = time.time() execution_time = (end_time - start_time)*1000 print("time is %d ms" % execution_time) return wrapper @deco def f(a,b): print("be on") time.sleep(1) print("result is %d" %(a+b)) if __name__ == '__main__': f(3,4)
Extension 2: Decorators without Fixed Parameters
import time def deco(f): def wrapper(*args, **kwargs): start_time = time.time() f(*args, **kwargs) end_time = time.time() execution_time_ = (end_time - start_time)*1000 print("time is %d ms" %execution_time) return wrapper @deco def f(a,b): print("be on") time.sleep(1) print("result is %d" %(a+b)) @deco def f2(a,b,c): print("be on") time.sleep(1) print("result is %d" %(a+b+c)) if __name__ == '__main__': f2(3,4,5) f(3,4)
Extension 3: Decorate a function with multiple decorators
import time def deco01(f): def wrapper(*args, **kwargs): print("this is deco01") start_time = time.time() f(*args, **kwargs) end_time = time.time() execution_time = (end_time - start_time)*1000 print("time is %d ms" % execution_time) print("deco01 end here") return wrapper def deco02(f): def wrapper(*args, **kwargs): print("this is deco02") f(*args, **kwargs) print("deco02 end here") return wrapper @deco01 @deco02 def f(a,b): print("be on") time.sleep(1) print("result is %d" %(a+b)) if __name__ == '__main__': f(3,4)
Decorator Call Order
Decorator can be superimposed, so what is the sequence of code after using decorator?
For the "@" grammatical sugar in Python, the order in which the decorator is invoked is the reverse of the order in which the @ grammatical sugar declaration is used.
In this example, "f (3, 4) = deco01 (deco02 (f (3, 4))".
Python built-in decorator
There are three built-in decorators in Python that are class-related: static method, class method, and property.
staticmethod is a class static method, which differs from member methods in that it has no self parameter and can be invoked without instantiating the class.
The difference between classmethod and member method is that the first parameter received is not self (a pointer to a class instance), but cls (the specific type of the current class).
property is what attributes mean and represents information that can be accessed directly through class instances.
For static method and classmethod, we don't introduce them here. Let's look at property through an example.
Note that for the Python new-style class, if the member function decorated by the @var.setter decorator above is removed, the Foo.var attribute is read-only, and an exception is thrown when assigning with "foo.var='var 2'". However, for Python classic class, the declared attribute is not read-only, so even removing the "@var.setter" decorator will not cause an error.
summary
This paper introduces some uses of Python decorator. The code of the decorator is easy to understand. It's easy to understand if you use some examples to do the actual operation.