python装饰器的又一特性
在之前一篇关于装饰器的文章中,介绍了装饰器的基本用法。
1 def deco(render=None): 2 def wrap(func): 3 def wrapper(*args,**kwargs): 4 result = func(*args,**kwargs) ### 5 return render(result) ### 6 return wrapper 7 return wrap 8 9 10 my_render = lambda x: str(x) + ' --my_render' 11 12 @deco(render=my_render) 13 def test(): 14 return "this is test!"
其中的过程大概是这样,最外层的函数deco,如果加上参数,说明运行这个函数并返回。
最终这个装饰器返回的是wrapper,而不是第二层的wrap。为什么呢?
究其原因,还是因为其实装饰器是闭包的一种特殊形式,闭包只有两层。
典型的装饰器也只有两层,但如果想给装饰器加参数,就把接受参数的函数放到最外层。
最外层的参数执行deco后,返回的是里层的两个函数,wrap和wrapper。
被装饰的函数test执行后,返回了有最内层函数wrapper装饰后的结果。
有一个特殊需求,我们想在被装饰的函数test还没有运行的时候,就完全执行装饰器。就是将三层函数都运行。
记住一点:闭包只有两层,最外层是接受被装饰的函数。内层函数是被装饰的函数执行时运行的,它接受被装饰函数的参数。
下面看一个例子,这个例子中有嵌套了四层:
1 def wrap(t=1): 2 print "in wrap" 3 4 def wrapper(m=2): 5 print "in wrapper" 6 print "m is ", m 7 8 def wrapper_1(func): 9 print "in wrappper_1" 10 11 def last_one(*args, **kwargs): 12 print "in last_one" 13 return func(*args, **kwargs) 14 return last_one 15 16 return wrapper_1 17 18 return wrapper(33) 19 20 @wrap() 21 def test(): 22 print "test"
最外层的wrap函数接受装饰器的参数。
比较特别的是return wrapper(33)这一句,它将wrapper_1和last_one函数作为一个装饰器返回。
看看运行结果:
>>> from test_decorator import test in wrap in wrapper m is 33 in wrappper_1 >>> >>> test() in last_one test >>> >>> test <function last_one at 0x7f196e0548c0>
导入test函数后,最终装饰test函数的是last_one函数。
但我们还没有达到刚才提到的需求,我们想让被装饰的函数运行前,装饰器全部执行完毕。
其实很简单,只要将函数改为三层嵌套,在第一层函数结束时调用执行第二层函数。
给出最后的代码:
def wrap(t=1): print "in wrap" def wrapper(m=2): print "in wrapper" print "m is ", m def wrapper_1(func): print "in wrappper_1" return func return wrapper_1 return wrapper(33) @wrap() def test(): print "test"
看看它的执行结果:
In [1]: from test_decorator_1 import test in wrap in wrapper m is 33 in wrappper_1 In [2]: test() test
看到了吧,我们的装饰器完全执行了。
其实只是在last_one函数里将test函数原样返回而已, 而且因为少了一层函数,所以就不能接收被装饰函数的参数了。
但是这样也有一个特殊的应用场景,例如在原样返回test函数前,将test函数用其他方式生成新的对象并返回。
如此以来当import完毕的时候,test函数已经变成另外一个对象了。可以变成一个类,等等。。。