Python装饰器本质是用新函数替换原函数对象,定义时立即执行赋值操作;需用functools.wraps显式继承元信息,多层装饰器按@顺序嵌套替换。

Python 装饰器的本质,就是用一个新函数替换原函数对象,同时保留原函数的名称、文档字符串等元信息(需显式处理)。它不是语法糖的幻觉,而是一次明确的赋值操作:把被装饰函数传给装饰器,再用返回值覆盖原函数名所指向的对象。
装饰器执行时机:定义时即完成替换
装饰器在函数定义后立即执行,不是调用时才生效。@decorator 写在 def 上方,等价于 func = decorator(func) —— 这行赋值在模块加载时就完成了。
例如:
def my_decorator(f):
print("装饰器运行了")
def wrapper(*args, **kwargs):
return f(*args, **kwargs) + 1
return wrapper
@my_decorator
def add(x):
return x + 1
运行这段代码,会立刻打印“装饰器运行了”,说明替换动作发生在 add 函数创建完成的那一刻,而非调用 add() 时。
立即学习“Python免费学习笔记(深入)”;
被替换的是函数对象,不是函数体
Python 中函数是对象,变量名只是引用。装饰器改变的是引用目标,原函数体仍存在内存中(只要还有其他引用),但通过原名已无法直接访问。
- add 现在指向 wrapper,不再是原始的函数对象
- 若想保留原始函数,可在装饰器内显式保存,如 wrapper.__wrapped__ = f
- inspect.getsource(add) 可能报错或返回 wrapper 的源码,而非原始函数体
名字和元信息不会自动继承
直接替换后,wrapper.__name__ 是 "wrapper",不是 "add";wrapper.__doc__ 为空,不继承原函数的文档字符串。这是初学者常踩的坑。
解决方式有两种:
- 手动复制:
wrapper.__name__ = f.__name__、wrapper.__doc__ = f.__doc__ - 更推荐用 functools.wraps(f):它是一个装饰器工厂,自动完成所有元信息同步
from functools import wrapsdef my_decorator(f): @wraps(f) # 关键:让 wrapper 继承 f 的 name、doc 等 def wrapper(*args, *kwargs): return f(args, **kwargs) + 1 return wrapper
多层装饰器 = 嵌套函数替换
@dec1
@dec2
def f(): ...
等价于 f = dec1(dec2(f)),即先用 dec2 替换 f,再用 dec1 替换 dec2(f) 的返回值。最外层装饰器最先执行,但包裹逻辑是最里层函数最先运行。
- dec2 先被调用,返回 inner2
- dec1 接收 inner2 作为参数,返回 inner1
- 最终 f 指向 inner1,调用时执行顺序:inner1 → inner2 → 原函数










