被装饰函数的__name__和__doc__丢失,是因为装饰器用wrapper新函数对象替换了原函数,而wrapper自带独立元数据;functools.wraps通过在wrapper上加@wraps(func)装饰,自动复制__module__、__name__、__qualname__、__doc__、__annotations__等关键属性,修复元信息。

为什么被装饰函数的 __name__ 和 __doc__ 会丢失?
Python 装饰器本质是用新函数替换了原函数对象,而新函数(闭包内部的 wrapper)有自己的 __name__、__doc__ 等属性,和原函数完全无关。不加处理时,调用 help(my_func) 或检查 my_func.__name__ 得到的都是 wrapper 的信息,不是你写的那个函数。
怎样用 functools.wraps 正确复制元数据?
functools.wraps 是个工厂函数,它接收原函数作为参数,返回一个修饰 wrapper 的装饰器,自动把原函数的关键属性复制过去。关键点在于:必须把它用作装饰器作用于内部的 wrapper 函数,而不是直接套在最外层装饰器上。
常见错误写法:@wraps(func) 错误地加在了装饰器定义上;正确位置是在闭包里、wrapper 函数定义前:
from functools import wraps
<p>def my_decorator(func):
@wraps(func) # ✅ 这里才对
def wrapper(*args, *<em>kwargs):
print("before")
result = func(</em>args, **kwargs)
print("after")
return result
return wrapper
这样之后:my_decorated_func.__name__ 就等于原函数名,my_decorated_func.__doc__ 也能正常显示文档字符串。
立即学习“Python免费学习笔记(深入)”;
functools.wraps 默认复制哪些属性?
它默认同步以下 5 个属性:__module__、__name__、__qualname__、__doc__、__annotations__。这些覆盖了绝大多数调试、IDE 提示、反射(如 inspect.signature)所需的信息。
如果你还依赖其他属性(比如自定义的 __version__ 或 __author__),wraps 不会自动处理,需要手动赋值:
@wraps(func)
def wrapper(*args, **kwargs):
...
wrapper.__version__ = func.__version__
不加 @wraps 会导致什么实际问题?
除了 help() 显示错乱,还会直接影响依赖函数签名的工具:
- Flask / FastAPI 路由注册可能失败(它们靠
__name__去重或生成 endpoint 名) -
inspect.signature(func)返回的是 wrapper 的签名,不是原函数的,导致类型检查或自动文档生成出错 - 单元测试中用
mock.patch时,如果 patch 目标是被装饰函数,没@wraps可能导致 mock 失效(因为名字对不上) - 某些序列化框架(如 Pydantic 的
validate_arguments)会因元数据缺失抛ValueError: no signature found
真正麻烦的不是“看起来不对”,而是下游工具链静默失效——等你发现时,往往已经绕了很大一圈。


















