不加 functools.wraps 会导致被装饰函数的 name__、__doc 等元信息丢失,因其被 wrapper 函数对象替代;@wraps(func) 必须作用于内部 wrapper 函数,才能正确复制原函数元数据。

为什么直接写装饰器会导致函数名和文档丢失
不加 functools.wraps 的装饰器,本质是用新函数替换了原函数对象。调用 help()、inspect.getdoc() 或访问 __name__ 时,拿到的都是内层闭包函数的信息,不是被装饰函数的。比如原函数叫 fetch_data,装饰后 fetch_data.__name__ 变成 wrapper,文档字符串也为空。
functools.wraps(func) 的正确用法
functools.wraps 是个工厂函数,它返回一个装饰器,用来修饰你写的包装函数(即 wrapper)。关键点在于:必须把它用在 def wrapper(...): 上面,而不是最外层的装饰器函数上。
常见错误写法:@wraps 加在最外层函数(接收 func 的那个)上——这没意义,因为那不是被调用的函数。
正确结构如下:
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</p>
这样 wrapper 就会复制 func 的 __name__、__doc__、__module__、__annotations__ 和 __dict__(部分)等属性。
不加 wraps 会出什么具体问题
这些现象在调试、测试和工具链中很常见:
-
help(my_decorated_func)显示的是wrapper的空帮助,而非原函数文档 - 单元测试中用
self.assertEqual(func.__name__, "expected_name")会失败 - FastAPI / Flask 等框架依赖
__name__和__doc__生成 API 文档,缺失会导致文档为空或路由名异常 -
inspect.signature(my_decorated_func)可能返回(*args, **kwargs)而非真实参数签名(wraps不自动复制 signature,需额外处理)
进阶注意:wraps 不解决所有元信息问题
functools.wraps 复制的是标准元数据,但有些信息它不管:
-
__signature__不会被自动更新 —— 如果需要保留原始签名(比如类型检查或 IDE 提示),得手动设:wrapper.__signature__ = inspect.signature(func) - 自定义属性如
func.version = "1.2"不会被复制,除非显式赋值:wrapper.version = func.version - 如果原函数有
__wrapped__属性(比如已被其他装饰器包装过),wraps不会链式展开,只取传入的func
真正容易被忽略的是:哪怕加了 @wraps(func),如果忘了 return wrapper,或者在 wrapper 里没调用 func,元信息“保留”了,功能却丢了——元信息只是让函数看起来像原来那样,不等于行为也一样。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











