functools.wraps 不可省略,否则被装饰函数的 name__、__doc__、__module__、__qualname__、__annotations 等元信息会丢失,导致调试、文档生成、类型检查等失败。

为什么 functools.wraps 不能省?
不加 functools.wraps 的装饰器,会让被装饰函数的元信息(__name__、__doc__、__module__ 等)变成内层包装函数的信息,导致调试、文档生成、反射等场景出错。这不是风格问题,是实际功能断裂。
functools.wraps 到底复制了哪些属性?
它本质是调用 update_wrapper,默认同步以下关键属性:
- __module__:确保 help() 和日志里显示正确的模块路径
- __name__:让断点调试、性能分析工具(如 cProfile)能识别原函数名
- __qualname__:支持嵌套函数和类方法的正确路径表示
- __doc__:保证 help() 和 IDE 悬停提示显示原始 docstring
- __annotations__:类型提示不会丢失,对 mypy、pylance 等工具至关重要
不加 @wraps(func) 的典型错误现象
常见于日志、测试、框架集成等依赖函数元信息的场景:
- help(my_decorated_func) 显示的是 wrapper 的空 docstring,而非原函数说明
- 单元测试中用 self.assertEqual(my_func.__name__, "expected_name") 直接失败
- FastAPI 或 Flask 路由函数丢失 __name__ 后,自动文档(Swagger)无法正确归类接口
- 使用 inspect.signature() 获取参数时,返回的是包装函数的签名,不是原始函数的
一个最小可验证示例
对比加与不加 @wraps 的行为差异:
from functools import wraps
<p>def log_calls(func):
def wrapper(*args, *<em>kwargs):
print(f"Calling {func.<strong>name</strong>}")
return func(</em>args, **kwargs)</p><h1>缺少 @wraps(func) → 元信息丢失</h1><pre class="brush:php;toolbar:false;">return wrapperdef log_calls_fixed(func): @wraps(func) def wrapper(*args, *kwargs): print(f"Calling {func.name}") return func(args, **kwargs) return wrapper
@log_calls def add(a, b): """Return sum of a and b.""" return a + b
@log_calls_fixed def multiply(a, b): """Return product of a and b.""" return a * b
执行 print(add.__name__, add.__doc__) 输出 wrapper None;而 multiply.__name__ 是 multiply,multiply.__doc__ 正确为 "Return product of a and b."。
元信息丢失的问题在简单脚本里可能无感,但一旦进入团队协作、自动化工具链或框架集成环节,就会变成难以定位的隐性故障点。
立即学习“Python免费学习笔记(深入)”;










