0. 一句话定位
| 维度 | 内容 |
|---|---|
| 作用对象 | 函数(装饰器内部的 wrapper) |
| 使用场景 | 元编程 |
| 来源 | 标准库 functools.wraps |
| 语法形式 | @wraps(wrapped) |
1. 做什么
在自定义装饰器的 wrapper 上复制被装饰函数的 __name__、__doc__、__module__、__annotations__、__qualname__ 等,使 help()、调试栈、文档生成仍指向原函数。
2. 重点参数
| 参数 | 类型 | 默认值 | 作用 | 配置建议 |
|---|---|---|---|---|
wrapped |
callable | 必填 | 被装饰的原函数 | 写 @wraps(fn),fn 为外层参数 |
assigned |
tuple | 标准元数据字段集 | 要复制的属性名 | 一般不改 |
updated |
tuple | __dict__ 等 |
额外更新的属性 | 一般不改 |
3. 最小可运行示例
1 | from functools import wraps |
无 @wraps 对比:
1 | def bad_decorator(fn): |
4. 常见变体
wraps + 带参装饰器
1 | def log_calls(enabled=True): |
类装饰器:对 __call__ 方法同样适用 @wraps。
5. 适用 / 不适用
适用
- 所有对外暴露的自定义装饰器(项目内通用约定)
- 需要
unittest.mock.patch('module.func')按原函数名打桩
不适用
- 故意隐藏原函数身份的重包装(极少见)
6. 易踩坑
- 忘记
@wraps后pytest收集、functools.partial链式调试困难 @wraps(fn)须装饰最内层 wrapper,不要装饰外层 factory- 异步 wrapper 同样要
@wraps(fn)
7. 近邻替代
| 替代 | 何时用 |
|---|---|
手动 wrapper.__name__ = fn.__name__ |
只复制个别字段,不推荐 |
| 不写 wraps | 仅一次性脚本、不 care 元数据 |
8. 参考
- Python 文档:functools.wraps
- 自定义装饰器完整示例见 00.概念-装饰器机制