装饰器本质是函数调用链的语法糖,即@decorator等价于func = decorator(func),要求装饰器返回可调用对象,多层装饰器执行顺序自下而上、包装顺序自上而下。

装饰器本质是函数调用链的语法糖
Python 装饰器不是魔法,它只是把 @decorator 写法自动转成 func = decorator(func)。关键在于:被装饰函数会被当作参数传给装饰器,而装饰器必须返回一个可调用对象(通常是新函数)。如果返回非可调用对象,后续调用会直接报 TypeError: 'NoneType' object is not callable。
- 装饰器本身可以是普通函数,也可以是类(需实现
__call__) -
@decorator必须出现在函数定义的正上方,且紧邻,中间不能有空行或注释 - 多个装饰器叠加时,执行顺序是自下而上(即离函数定义最近的先执行),但包装顺序是自上而下
带参数的装饰器必须嵌套三层函数
当你看到 @retry(max_attempts=3) 这种写法,说明这不是装饰器本身,而是「装饰器工厂」——它返回真正的装饰器。这类结构强制要求三层嵌套:外层接收装饰器参数,中层接收被装饰函数,内层是实际执行逻辑。
def retry(max_attempts=3):
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if i == max_attempts - 1:
raise
return None
return wrapper
return decorator- 漏掉任意一层(比如只写两层),会导致
TypeError: decorator() missing 1 required positional argument: 'func' - 常见误操作:在最外层直接写
func(*args, **kwargs),这会让装饰器立即执行,而不是延迟到函数调用时 - 使用
functools.wraps(func)包裹内层wrapper,否则被装饰函数的__name__、__doc__会变成wrapper的信息
类装饰器更适合管理状态和复用逻辑
当需要在多次调用间共享状态(如计数、缓存、连接池),类装饰器比闭包更清晰。它的核心是实现 __init__(接收被装饰函数)和 __call__(替代原函数行为)。
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"{self.func.__name__} has been called {self.count} times")
return self.func(*args, **kwargs)@CountCalls
def say_hello():
return "hello"
- 类装饰器无法直接用
@functools.wraps,需手动复制元数据(如self.__name__ = func.__name__)或继承functools.update_wrapper - 若类中定义了
__get__方法,它还能支持方法装饰(即用于类内def),否则对实例方法会出错:缺少self参数 - 类装饰器实例是单例,同一装饰器作用于多个函数时,它们共享该实例的状态(除非你在
__init__中为每个函数单独建状态容器)
调试装饰器时最容易忽略的栈帧污染
装饰器会改变原始函数的调用栈,导致 traceback 显示的是 wrapper 而非真实函数名,给排查问题带来干扰。尤其在日志、监控、性能分析工具中,这种「假栈帧」会让定位变慢。
立即学习“Python免费学习笔记(深入)”;
- 用
import inspect; inspect.stack()查看当前调用栈,确认是否有多余的wrapper层 - 生产环境建议统一用
functools.wraps,不只是为了__name__,它还会同步__annotations__和__dict__ - 异步函数(
async def)不能直接用同步装饰器包装,否则会报RuntimeWarning: coroutine 'wrapper' was never awaited;必须写专门的async def wrapper并用await func()
真正难的不是写出能跑的装饰器,而是让它的行为在元编程层面保持透明、可测、可维护。多数线上 bug 都藏在装饰器对异常传播、上下文变量、协程生命周期的隐式修改里。










