对多个装饰器一个装饰器的修饰顺序有疑问,于是写了个测试用例测试了下,发现了一些有意思的事情,刚开始不明白,经过思考后有所悟,遂记下
多个装饰器修饰一个函数
首先先说结论:多个装饰器装饰一个函数的时候,装饰的时候是从下往上装饰(由内往外),执行的时候从上往下执行(由外往内)。
先看代码
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 的运行结果了