装饰器 · wraps

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from functools import wraps

def my_decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
return fn(*args, **kwargs)
return wrapper

@my_decorator
def greet(name: str) -> str:
"""向 name 打招呼"""
return f"Hello, {name}"

print(greet.__name__) # greet(无 wraps 则为 wrapper)
print(greet.__doc__) # 向 name 打招呼

@wraps 对比:

1
2
3
4
5
6
7
8
9
10
11
def bad_decorator(fn):
def wrapper(*args, **kwargs):
return fn(*args, **kwargs)
return wrapper

@bad_decorator
def foo():
"""doc"""
pass

print(foo.__name__) # wrapper

4. 常见变体

wraps + 带参装饰器

1
2
3
4
5
6
7
8
9
def log_calls(enabled=True):
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
if enabled:
print(f"call {fn.__name__}")
return fn(*args, **kwargs)
return wrapper
return decorator

类装饰器:对 __call__ 方法同样适用 @wraps

5. 适用 / 不适用

适用

  • 所有对外暴露的自定义装饰器(项目内通用约定)
  • 需要 unittest.mock.patch('module.func') 按原函数名打桩

不适用

  • 故意隐藏原函数身份的重包装(极少见)

6. 易踩坑

  • 忘记 @wrapspytest 收集、functools.partial 链式调试困难
  • @wraps(fn) 须装饰最内层 wrapper,不要装饰外层 factory
  • 异步 wrapper 同样要 @wraps(fn)

7. 近邻替代

替代 何时用
手动 wrapper.__name__ = fn.__name__ 只复制个别字段,不推荐
不写 wraps 仅一次性脚本、不 care 元数据

8. 参考

-------------本文结束感谢您的阅读-------------