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函数已经变成另外一个对象了。可以变成一个类,等等。。。

原文地址:https://www.cnblogs.com/huazi/p/2842250.html