在Python里使用多个装饰器修饰同一个函数时,装饰器的执行顺序有固定的规则,理解这个规则能帮助开发者正确设计装饰器的叠加逻辑。当代码中先写@dec1再写其他装饰器时,装饰器的加载和函数调用阶段的执行顺序存在差异,下面通过具体示例说明。

装饰器的基础原理回顾
装饰器本质是高阶函数,它接收被修饰的函数作为参数,返回一个替代原函数的新函数。当我们使用@dec1修饰函数func时,等价于执行func = dec1(func)的操作。多个装饰器叠加时,这个替换过程会依次执行。
多个装饰器叠加的执行顺序
假设我们有两个装饰器dec1和dec2,按照如下方式修饰函数:
# 定义第一个装饰器
def dec1(func):
def wrapper1(*args, **kwargs):
print("进入dec1的wrapper")
result = func(*args, **kwargs)
print("离开dec1的wrapper")
return result
return wrapper1
# 定义第二个装饰器
def dec2(func):
def wrapper2(*args, **kwargs):
print("进入dec2的wrapper")
result = func(*args, **kwargs)
print("离开dec2的wrapper")
return result
return wrapper2
# 多个装饰器叠加修饰函数,先写@dec1,再写@dec2
@dec1
@dec2
def test_func():
print("执行test_func主体逻辑")
return "test_result"
# 调用被修饰后的函数
if __name__ == "__main__":
res = test_func()
print("函数返回值:", res)
运行上述代码后,输出结果如下:
进入dec1的wrapper 进入dec2的wrapper 执行test_func主体逻辑 离开dec2的wrapper 离开dec1的wrapper 函数返回值: test_result
加载阶段的顺序
多个装饰器叠加时,加载阶段(也就是函数定义的时候)的执行顺序是从下往上的。上面的代码中,@dec1写在@dec2的上方,实际加载时先执行test_func = dec2(test_func),再执行test_func = dec1(test_func)。最终得到的test_func其实是dec1返回的wrapper1函数,而wrapper1内部调用的func是dec2返回的wrapper2函数,wrapper2内部调用的func才是原始的test_func。
调用阶段的顺序
当调用被修饰后的test_func时,执行顺序是从上往下的。首先进入dec1的wrapper1,然后进入dec2的wrapper2,接着执行原始函数逻辑,之后先退出dec2的wrapper2,最后退出dec1的wrapper1,整体呈现类似嵌套的结构。
装饰器带参数的情况
如果装饰器本身带参数,执行顺序的规则依然一致,只是多了一层参数接收的逻辑。示例如下:
# 带参数的装饰器dec1
def dec1(param):
def outer1(func):
def wrapper1(*args, **kwargs):
print(f"dec1参数:{param},进入dec1的wrapper")
result = func(*args, **kwargs)
print("离开dec1的wrapper")
return result
return wrapper1
return outer1
# 带参数的装饰器dec2
def dec2(param):
def outer2(func):
def wrapper2(*args, **kwargs):
print(f"dec2参数:{param},进入dec2的wrapper")
result = func(*args, **kwargs)
print("离开dec2的wrapper")
return result
return wrapper2
return outer2
# 叠加使用带参数的装饰器
@dec1("dec1_param")
@dec2("dec2_param")
def test_func2():
print("执行test_func2主体逻辑")
if __name__ == "__main__":
test_func2()
上述代码的输出结果为:
dec1参数:dec1_param,进入dec1的wrapper dec2参数:dec2_param,进入dec2的wrapper 执行test_func2主体逻辑 离开dec2的wrapper 离开dec1的wrapper
总结
多个装饰器叠加时,记住两个核心规则即可:加载阶段从下往上依次执行装饰器函数,调用阶段从上往下依次执行装饰器的内部包装逻辑。先写的@dec1会在加载阶段最后执行,在调用阶段最先执行。开发者可以根据这个规则合理设计多个装饰器的执行逻辑,满足不同的业务需求。