不加functools.wraps时,装饰器用wrapper函数替换原函数,导致__name__、__doc__等属性被覆盖为wrapper的值;加上后,wraps通过update_wrapper将原函数的__name__、__doc__、__module__等7个关键属性复制到wrapper,并设置__wrapped__指向原函数,从而透传元信息。

不加 functools.wraps,被装饰函数的 __name__、__doc__、__module__ 等全会变成装饰器内部函数的——这不是 bug,是默认行为;加了它,才能“透传”原函数的元信息。
为什么直接写装饰器会丢掉 __doc__ 和 __name__
Python 装饰器本质是用新函数替换了原函数对象。如果你没手动复制元信息,Python 就只认新函数的属性:
- 调用
help(my_func)显示的是装饰器里wrapper的 docstring,不是my_func自己写的 -
my_func.__name__变成'wrapper',而不是'my_func' - 某些依赖函数名的框架(如 Flask 路由注册、pytest 参数化)可能出错
怎么正确用 functools.wraps
它是个装饰器工厂,接收原函数作为参数,返回一个用于修饰 wrapper 的装饰器:
from functools import wraps
<p>def my_decorator(func):
@wraps(func) # ← 关键:套在 wrapper 上
def wrapper(*args, *<em>kwargs):
print("before")
result = func(</em>args, **kwargs)
print("after")
return result
return wrapper这样 wrapper 就会自动继承 func 的 __name__、__doc__、__module__、__annotations__、__dict__ 等关键属性。
立即学习“Python免费学习笔记(深入)”;
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
@wraps(func) 复制了哪些属性?哪些没复制?
它复制的是标准库定义的「可安全继承」的元信息,包括:
-
__name__、__qualname__ -
__doc__(即 docstring) -
__module__、__annotations__ -
__dict__中的非私有项(但注意:不会覆盖已存在的同名 key)
它**不复制**:
-
__closure__(闭包变量)——这是函数执行时动态生成的,没法也不该继承 -
__code__(字节码)——wrapper 必须有自己的逻辑 - 自定义的特殊属性(比如你手动加的
func.version = '1.2'),得自己赋值
常见踩坑点:@wraps 放错位置或漏掉括号
错误写法会导致 TypeError: 'function' object is not callable 或完全没效果:
- ❌ 写成
@wraps(没传func):装饰器没拿到目标函数,无法初始化 - ❌ 把
@wraps(func)放在def wrapper外面(比如放在def my_decorator上):作用对象错了 - ❌ 在类方法装饰器里传错参数:要传
func,不是self或cls - ❌ 多层装饰器时,只在最内层用
@wraps:外层装饰器仍会覆盖元信息,每层 wrapper 都得各自@wraps
真正容易被忽略的是:当装饰器本身带参数(比如 @retry(times=3)),@wraps 必须落在最内层的 wrapper 上,且传入的是最终要包装的那个函数——这需要三层嵌套,稍不注意就漏掉。

















