应使用functools.wraps(func)装饰内层wrapper函数,它自动同步__name__、__doc__等元数据并设置__wrapped__属性,避免help()显示错误、文档生成失败等问题。

装饰器导致help()和__name__失效怎么办
不加处理的装饰器会让被装饰函数的元数据(比如__name__、__doc__、__module__)变成内层闭包函数的信息,help(func)会显示错误签名,func.__name__变成'wrapper',调试和自动文档生成直接出问题。
根本原因是装饰器返回的是新函数(通常是闭包里的wrapper),它和原函数完全无关。解决方式不是重写装饰器逻辑,而是用functools.wraps做元数据同步:
from functools import wraps
<p>def my_decorator(func):
@wraps(func) # ← 关键:把func的元数据复制给wrapper
def wrapper(*args, *<em>kwargs):
print("before")
result = func(</em>args, **kwargs)
print("after")
return result
return wrapper</p>
@wraps(func)本质是调用update_wrapper(wrapper, func),把func的__name__、__doc__、__module__、__annotations__等拷贝到wrapper上。
哪些元数据会被functools.wraps保留
functools.wraps默认只同步一组常用字段,不是全部。它实际等价于:update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)。
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__', '__annotations__')WRAPPER_UPDATES = ('__dict__',)
也就是说:__defaults__、__kwdefaults__、__closure__这些不会被复制——它们属于函数运行时属性,不是“元数据”。如果你依赖inspect.signature()获取参数信息,它仍然能正确解析原函数,因为signature()读的是函数对象本身的__code__,不受wraps影响。
没加@wraps时的典型报错现象
常见于框架集成场景,比如 FastAPI 要求路由函数有明确__name__和__doc__来生成 OpenAPI 文档,或 pytest 依赖__name__识别测试函数名。
安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。
典型表现包括:
-
help(my_decorated_func)显示wrapper(*args, **kwargs)而非原始签名 -
my_decorated_func.__name__返回'wrapper',而非真实函数名 -
my_decorated_func.__doc__为None,即使原函数写了 docstring - 使用
inspect.getfullargspec(my_decorated_func)可能抛ValueError: Function has no argument info(极少见,但发生在某些自定义装饰器未正确传递__wrapped__时)
为什么不能手动赋值__name__和__doc__
可以,但不推荐。手动同步容易遗漏字段,比如忘了__qualname__或__annotations__,而这些在 Python 3.10+ 的类型检查、IDE 补全中很关键。
更严重的问题是:functools.wraps还会设置__wrapped__属性,指向原始函数——这是很多工具(如inspect.unwrap()、debugpy断点跳转)依赖的约定。手动赋值做不到这点。
所以哪怕只差一行@wraps(func),也别省。
真正容易被忽略的是:如果装饰器本身带参数(即三层嵌套),@wraps必须放在最内层wrapper上,而不是中间的decorator_factory里。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










