对多个装饰器一个装饰器的修饰顺序有疑问,于是写了个测试用例测试了下,发现了一些有意思的事情,刚开始不明白,经过思考后有所悟,遂记下
多个装饰器修饰一个函数
首先先说结论:多个装饰器装饰一个函数的时候,装饰的时候是从下往上装饰(由内往外),执行的时候从上往下执行(由外往内)。
先看代码
def desc1(func):
def inner1(*args,**kwargs):
print('desc1')
func(*args,**kwargs)
return inner1
def desc2(func):
def inner2(*args,**kwargs):
print('desc2')
func(*args,**kwargs)
return inner2
@desc1 #
@desc2 #add=inner2(会携带打印desc2)
def add(x,y):
print(x+y)
add(1,2)
可以看到装饰的时候 desc2
先装饰 add
函数,接着作为一个整体,被 desc1
装饰。执行的时候,由外往内,依次执行 desc1
和 desc2
的装饰(添加的打印语句),最后执行函数本身。
一点有意思的事
看到两个装饰器中都执行了 func(*args,**kwargs)
,本能的反应是最终会2次执行函数,也就是打印两个3。然后我分别删除了 desc1
和 desc2
中的 func
函数,得到的结果如下:
删除 desc1
的 func
函数
删除 desc2
的 func
函数
这是怎么回事,删除 desc1
的 func
后 desc2
竟然还不能装饰了?
仔细思考后,原来是这样的:
装饰器的作用就是在不修改原函数不影响原函数的功能的基础上,给函数增加额外的功能。
首先说一下装饰器是如何给原函数增加功能的:
装饰器 装饰一个函数也就是写到函数上方 @desc2
,这种语法糖的写法等价于 add = desc(func)
。也就是把被修饰的函数当作参数传递到了装饰器中,而装饰器返回内部函数 inner2
,于是 add=inner2
,再执行 add
的时候就是执行 inner2
函数,这时候可以在 inner2
中增加额外的功能比如增加打印信息,完了在原函数执行本身,从而达到了给原函数增加功能的目的。
再看一下如何理解以上输出结果:
装饰器返回内部函数 inner2
的时候,可以认为是给原函数 add
携带了 print('desc2')
这种特质,他和原函数 func(*args,**kwargs)
整体作为一个函数,被 desc1
修饰,而 desc1
也是包装了一个 print('desc1')+func(*args,**kwargs)
的形式,这个 func(*args,**kwargs)
被 内部的 刚刚那个 print('desc2')+func(*args,**kwargs)
占据,画成图形来理解的话就像是这样
于是执行 add
的时候先执行 print('desc1')
,接着执行内部的 func
函数,而 func
函数实际就是
@desc2
def add(x,y):
于是执行 print('desc2')
,最后执行 内部的 add
函数。这样,就只会执行一次原函数。
那么为什么删除 desc1
的 func
就只输出 'desc1'
呢?
删除 desc1
的 func
就相当于删除了 desc1
装饰器的 func
盒子,相当于删除了被 desc2
修饰的函数整体,自然不能执行 desc2
修饰的函数了
为什么删除 desc2
的 func
就只输出 'desc1'
和 'desc2'
呢?
删除 desc2
的 func
就相当于删除了 desc2
的 func
盒子,desc2
的 func
盒子中装的是原函数 add
,自然不会输出 add
的运行结果了