Functools -- wraps
wraps 是一个装饰器,用于 编写装饰器时保持被包装函数的元数据,比如:
__name____doc____module____annotations____wrapped____qualname__
否则,使用装饰器会导致函数被替换为内部 wrapper,丢失原信息。
例子:
def logger(func):
def wrapper(*args, **kwargs):
print("call", func.__name__)
return func(*args, **kwargs)
return wrapper
@logger
def add(a, b):
"""加法函数"""
return a + b
print(add.__name__)
print(add.__doc__)
输出:
wrapper
None
原函数 add 的名字和文档都丢失了。
from functools import wraps
def logger(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("call", func.__name__)
return func(*args, **kwargs)
return wrapper
@logger
def add(a, b):
"""加法函数"""
return a + b
print(add.__name__)
print(add.__doc__)
输出:
add
加法函数
相当于:
- 把原函数的名称、注释、文档写回包装函数
- 给包装函数增加
__wrapped__= func,支持 inspect 解包 - 让 IDE / 文档工具继续识别真实信息
底层等价于:
def wraps(func):
def decorator(wrapper):
wrapper.__name__ = func.__name__
wrapper.__doc__ = func.__doc__
wrapper.__module__ = func.__module__
wrapper.__annotations__ = func.__annotations__
wrapper.__wrapped__ = func
return wrapper
return decorator
你今后写所有装饰器都用这个结构:
from functools import wraps
def my_decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
# 前置逻辑
result = fn(*args, **kwargs)
# 后置逻辑
return result
return wrapper
def log_call(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
print(f"[CALL] {fn.__name__}")
return fn(*args, **kwargs)
return wrapper
def cache(fn):
memo = {}
@wraps(fn)
def wrapper(x):
if x not in memo:
memo[x] = fn(x)
return memo[x]
return wrapper
def require_admin(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
user = kwargs.get("user")
if not user or user.role != "admin":
raise PermissionError("需要管理员权限")
return fn(*args, **kwargs)
return wrapper
装饰器后的函数 metadata 丢失,导致路径参数错误。
cache 会识别 wrapper,而不是原函数。
traceback 显示 wrapper,而不是真实函数名称。
函数注解丢失。
默认复制:
wraps(func)
你可以选择复制哪些属性:
wraps(func, assigned=("__name__", "__doc__"), updated=())
functools.wraps 用于编写装饰器。
作用:
- 保留原函数的
__name__ - 保留原函数的
__doc__ - 保留
__annotations__ - 保留
__module__ - 添加
__wrapped__,方便 inspect 解包
用法:
from functools import wraps
def deco(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
不使用的后果:
- debug 和堆栈变难
- IDE 无法提示参数
- FastAPI 文档不准确