Objective
The understanding of the Python decorator has been stuck in the hierarchy of "using decorators to register functions as handlers for events", which is also a smattering; this is not a way to do it, just to tidy up today about the Python decorator concept and usage.
Introduced
The adorner is a well-known design pattern, which is often used in scenes where there is a demand for facets, with the classic insert log, performance test, transaction processing, and so on. Decorators are a great design for solving such problems, and with adorners, we can pull out a lot of the same code that is not relevant to the function itself and continue to reuse it. In summary, the function of an adorner is to add additional functionality to an already existing object.
Function
Let's start with a simple example, this example is a question on stackflow, how to implement the output by using the following code <b><i>Hello</i></b> :
@makebold @makeitalic def say(): return "Hello"
First look at the answer:
def makebold(fn): def wrapped(): return "<b>" + fn() + "</b>" return wrapped def makeitalic(fn): def wrapped(): return "<i>" + fn() + "</i>" return wrapped @makebold @makeitalic def hello(): return "hello world"
Here @makebold and @makeitalic seems to add a layer of packaging (or decoration) to Hello, this is the most obvious embodiment of the decorator.
From the demand
Early on, I wrote a function
def foo(): print 'in foo()'
In order to check the complexity of this function (the delay of the program is very important in network programming), the computation time needs to be measured, and the function of calculating time is added with the following code:
import time def foo(): start = time.clock() print 'in foo()' end = time.clock() print 'Time Elapsed:', end - start foo()
Here just write a function, if I want to measure the time delay of multiple functions, because must know start and end, so must write at the beginning and end of the program, is every program like this copy paste? It is possible, however, that we can separate the function from the data part in the design pattern, separating the function of the measuring time, just like in C + + we can turn this time-measuring function into a class, by invoking this class and assigning different functions to measure the runtime of different functions. In Python, because a function is actually an object, a similar approach can be implemented:
import time def foo(): print 'in foo()' def timeit(func): start = time.clock() func() end =time.clock() print 'Time Elapsed:', end - start timeit(foo)
Func () Here can specify a function, but what if I don't want to fill out this function or the function function doesn't change to a similar form? What we need is minimal change:
import time def foo(): print 'in foo()' # 定义一个计时器,传入一个,并返回另一个附加了计时功能的方法 def timeit(func): # 定义一个内嵌的包装函数,给传入的函数加上计时功能的包装 def wrapper(): start = time.clock() func() end =time.clock() print 'Time Elapsed:', end - start # 将包装后的函数返回 return wrapper foo = timeit(foo) #可以直接写成@timeit + foo定义,python的"语法糖"foo()
In this code, Timeit (foo) does not directly produce the call effect, but instead returns a function that is consistent with the Foo parameter list, when this foois not foo! Because Foo has the Timeit effect at this point, it simply means that you can execute the code before and after the decoration without changing the contents of the function itself, the adorner is a function, and its argument is another function.
An interesting "burger" that lets you know the order
The order in the decorator is still very important, using a code to demonstrate:
def bread(func) : def wrapper() : print "</''' '''\>" func() print "<\______/>" return wrapper def ingredients(func) : def wrapper() : print "#tomatoes#" func() print "~salad~" return wrapper def sandwich(food="--ham--") : print food sandwich() #输出 : --ham-- sandwich = bread(ingredients(sandwich)) sandwich() #输出: #</''' '''\> # #tomatoes# # --ham-- # ~salad~ #<\______/>
With syntax sugar, the code can be more concise:
def bread(func) : def wrapper() : print "</''' '''\>" func() print "<\______/>" return wrapper def ingredients(func) : def wrapper() : print "#tomatoes#" func() print "~salad~" return wrapper @bread @ingredients def sandwich(food="--ham--") : print food sandwich()
Expand built-in decorators
There are three built-in decorators, Staticmethod, Classmethod, and property, respectively, to make the instance methods defined in the class into static, class, and class properties.
To modify a parameter with an argument function
If the original function has parameters, the closure function must keep the number of arguments consistent and pass the arguments to the original method
def w1(fun): def wrapper(name): print("this is the wrapper head") fun(name) print("this is the wrapper end") return wrapper@w1def hello(name): print("hello"+name)hello("world")# 输出:# this is the wrapper head# helloworld# this is the wrapper end
Multiple parameter tests:
def w2(fun): def wrapper(*args,**kwargs): print("this is the wrapper head") fun(*args,**kwargs) print("this is the wrapper end") return wrapper@w2def hello(name,name2): print("hello"+name+name2)hello("world","!!!")#输出:# this is the wrapper head# helloworld!!!# this is the wrapper end
Functions that have return values
def w3(fun): def wrapper(): print("this is the wrapper head") temp=fun() print("this is the wrapper end") return temp #要把值传回去呀!! return wrapper@w3def hello(): print("hello") return "test"result=hello()print("After the wrapper,I accept %s" %result)#输出:#this is the wrapper head#hello#this is the wrapper end#After the wrapper,I accept test
Modifiers with parameters
Directly on the code:
def func_args(pre='xiaoqiang'): def w_test_log(func): def inner(): print('...记录日志...visitor is %s' % pre) func() return inner return w_test_log# 带有参数的修饰器能够起到在运行时,有不同的功能# 先执行func_args('wangcai'),返回w_test_log函数的引用# @w_test_log# 使用@w_test_log对test_log进行修饰@func_args('wangcai')def test_log(): print('this is test log')test_log()#输出:#...记录日志...visitor is wangcai# this is test log
General modifiers
There is a decorator form for each type, how do you remember? So there's this "universal modifier":
def w_test(func): def inner(*args, **kwargs): ret = func(*args, **kwargs) return ret return inner@w_testdef test(): print('test called')@w_testdef test1(): print('test1 called') return 'python'@w_testdef test2(a):
Class Decorator
When an object is created and executed directly, it throws an exception because he is not a callable and cannot be executed directly, but after making modifications, it can execute the call directly:
class Test(object): def __call__(self, *args, **kwargs): print('call called')t = Test()print(t())# 就可以直接执行
To modify a class directly:
class Test(object): def __init__(self, func): print('test init') print('func name is %s ' % func.__name__) self.__func = func def __call__(self, *args, **kwargs): print('this is wrapper') self.__func()@Testdef test(): print('this is test func')test()#输出:# test init# func name is test # this is wrapper# this is test func
Postscript
First introduced here, the general also has a certain understanding of the decorator. Later, I will continue to study in combination with my project.
Talk about Python decorators