Python functools.wraps
functools.wraps 用于保留被装饰函数的元信息,避免装饰器覆盖原始函数属性。
问题演示
不使用 wraps 时的问题:
Python
def my_decorator(func):
def wrapper(*args, **kwargs):
"这是包装函数的文档"
return func(*args, **kwargs)
return wrapper
@my_decorator
def greet(name):
"向某人问好"
return f"Hello, {name}!"
print(greet.__name__) # wrapper(应该是 greet)
print(greet.__doc__) # 这是包装函数的文档(应该是 向某人问好)
使用 wraps 解决
Python
from functools import wraps
def my_decorator(func):
@wraps(func) # 保留被装饰函数的元信息
def wrapper(*args, **kwargs):
"这是包装函数的文档"
return func(*args, **kwargs)
return wrapper
@my_decorator
def greet(name):
"向某人问好"
return f"Hello, {name}!"
print(greet.__name__) # greet
print(greet.__doc__) # 向某人问好
wraps 的原理
wraps 实际上是 partial(update_wrapper, ...) 的快捷方式:
Python
from functools import wraps, update_wrapper, WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES
# 查看默认复制的属性
print(WRAPPER_ASSIGNMENTS)
# ('__module__', '__name__', '__qualname__', '__annotations__', '__doc__', '__wrapped__')
print(WRAPPER_UPDATES)
# ('__dict__',)
update_wrapper 函数
Python
from functools import update_wrapper
def my_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
# 手动更新属性
update_wrapper(wrapper, func)
return wrapper
常用属性保留
Python
from functools import wraps
def debug(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__}")
print(f"参数: {args}, {kwargs}")
print(f"模块: {func.__module__}")
return func(*args, **kwargs)
return wrapper
@debug
def calculate(a: int, b: int) -> int:
"计算两数之和"
return a + b
print(calculate.__name__) # calculate
print(calculate.__doc__) # 计算两数之和
print(calculate.__annotations__) # {'a': int, 'b': int, 'return': int}
print(calculate.__wrapped__) # 原始函数对象
带 wrapped 的妙用
使用 wraps 后,可以通过 __wrapped__ 访问原始函数:
Python
from functools import wraps
def strong_auth(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("权限验证...")
return func(*args, **kwargs)
return wrapper
@strong_auth
def delete_user(user_id):
"删除用户"
return f"已删除用户 {user_id}"
# 通过 __wrapped__ 调用原始函数(跳过装饰器)
delete_user.__wrapped__(1) # 直接调用,跳过权限验证
自定义保留属性
可以指定要保留的属性:
Python
from functools import wraps
def custom_decorator(func):
@wraps(func, assigned=('__name__', '__doc__'), updated=())
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
在类装饰器中使用
类装饰器同样需要保留元信息:
Python
from functools import wraps
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
wraps(func)(self) # 关键:保留元信息
def __call__(self, *args, **kwargs):
self.count += 1
return self.func(*args, **kwargs)
@CountCalls
def process(data):
"处理数据"
return data * 2
print(process.__name__) # process
print(process.__doc__) # 处理数据
常用装饰器模板
Python
from functools import wraps
# 无参数装饰器
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 前置逻辑
result = func(*args, **kwargs)
# 后置逻辑
return result
return wrapper
# 带参数装饰器
def decorator_with_args(arg1, arg2=None):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 使用 arg1, arg2
return func(*args, **kwargs)
return wrapper
return decorator
要点总结
| 属性 | 说明 |
|---|---|
__name__ | 函数名称 |
__doc__ | 文档字符串 |
__module__ | 所在模块 |
__annotations__ | 类型注解 |
__wrapped__ | 原始函数引用 |
| 要点 | 说明 |
|---|---|
| 位置 | 必须放在 wrapper 函数定义前 |
| 作用 | 复制函数元信息到包装函数 |
| 类装饰器 | 使用 wraps(func)(self) |
| 访问原函数 | 通过 __wrapped__ 属性 |
使用
@wraps(func)是编写规范装饰器的最佳实践,确保调试信息和文档正确显示。
D:\git2\jwdev\articles\PYTHON\进阶\装饰器深入\functools.wraps.md
📝 发现内容有误?点击此处直接编辑