装饰器执行分定义和调用两阶段:定义时自下而上包装,调用时自上而下执行;带参装饰器先执行工厂函数再包装;日志可清晰追踪调用链。

装饰器的执行顺序分两个阶段:定义时的包装顺序,和调用时的执行顺序。理解这两者的区别,是搞懂多层装饰器的关键。
装饰器定义时:从下往上包装
当你写多个装饰器叠加时,Python 会按从下到上的顺序应用它们(即离函数最近的先执行)。这决定了最终函数对象的结构。
例如:
@decorator_a
@decorator_b
@decorator_c
def my_func():
pass
等价于:
立即学习“Python免费学习笔记(深入)”;
my_func = decorator_a(decorator_b(decorator_c(my_func)))
也就是说,decorator_c 最先被调用,包装原函数;然后 decorator_b 包装 decorator_c 的返回值;最后 decorator_a 包装整个结果。
函数调用时:从上往下执行
虽然包装是自下而上,但真正调用 my_func() 时,执行流程是反过来的:最外层装饰器先拿到控制权,再一层层往里传。
- 调用
my_func()→ 进入decorator_a的 wrapper - wrapper 中调用
func()→ 实际是decorator_b的 wrapper - 再调用其内部
func()→ 实际是decorator_c的 wrapper - 最后才执行原始函数体
带参数的装饰器:多一层函数嵌套
像 @retry(max_attempts=3) 这类装饰器,本质是“装饰器工厂”,它返回真正的装饰器。
执行顺序仍是两阶段:
- 定义时:
retry(max_attempts=3)先运行,返回一个装饰器函数;该函数再按前述规则去包装目标函数 - 调用时:仍遵循外层 wrapper → 内层 wrapper → 原函数的链式调用路径
可以这样理解:每多一层 @,就多一次函数调用和一次包装;而带参装饰器在 @ 那一刻就完成了一次“预计算”。
调试技巧:加打印快速理清链条
在每个装饰器的 wrapper 开头和结尾加日志,能直观看到执行流:
def log_call(name):
def decorator(func):
def wrapper(*args, **kwargs):
print(f"[进入] {name}")
result = func(*args, **kwargs)
print(f"[退出] {name}")
return result
return wrapper
return decorator
@log_call("A")
@log_call("B")
@log_call("C")
def hello():
print("hello world")
调用 hello() 输出为:
[进入] A [进入] B [进入] C hello world [退出] C [退出] B [退出] A
清楚展示了“进时由外向内,出时由内向外”的调用栈行为。










