Python多个装饰器的作用顺序

如果一个函数应用了多个装饰器,那么这些装饰器被应用的先后顺序是:

  • 越靠近函数越被先调用,先被调用的装饰器的返回函数,会作为参数传递给后面调用的装饰器,即此装饰器上面的装饰器。

实验代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def need1(param: str):
def decorate(func):
def inner():
return func(param)
return inner
return decorate


def need2(func):
def inner(param):
print(param + 'need2')
return func()
return inner


@need1('need1')
@need2
def fu():
print('fu')


fu()

运行输出为:

1
2
need1need2
fu

其中need1装饰器需要一个具有一个参数的函数传入,need2装饰器需要一个无参函数,将两个装饰器按照上面顺序应用到一个无参函数上,可以被正确调用,说明need1正确接收到了具有一个参数的函数,即need2的返回值,证明首先调用了need2,得出越靠近函数的装饰器越被先调用。

若将两个装饰器位置调换,则输出为:

1
2
3
4
Traceback (most recent call last):
File "test.py", line 21, in <module>
fu()
TypeError: inner() missing 1 required positional argument: 'param'

inner被调用时缺少一个参数,即出错的位置在need2中,说明被传入need2的函数又一个参数,即need1的返回值被传入了need2中,首先调用了need1.

注意

这里的调用顺序是函数加载的时候的装饰器调用顺序,而不是这些装饰器生成函数后,各个装饰器的逻辑在生成的函数中被调用的顺序。

事实上各个装饰器的逻辑在被生成的函数中被调用的顺序是和这个顺序相反的,可以将目标函数想象成一个礼物,装饰器想象成一张张包装纸。在函数加载时最先被调用的装饰器,最为最内层包装被直接包装在礼物上,后面调用的装饰器按顺序被依次包装在上一层包装的外层,直到包装完毕。

而在装饰器生成的函数(包装好的礼物)被调用(拆开)的时候,是最外层的包装最先被撕掉,即最后被应用的装饰器中包含的逻辑最先被调用,再依次深入(拆开包装),直到目标函数(礼物)。


Python多个装饰器的作用顺序
https://maphical.cn/2021/07/python-decorator-order/
作者
MaphicalYng
发布于
2021年7月26日
许可协议