先看一个「能跑但有暗病」的装饰器:
def log(func):
def wrapper(*args, **kwargs):
print(f'call {func.__name__}')
return func(*args, **kwargs)
return wrapper
@log
def add(a, b):
"""加法"""
return a + b
print(add.__name__) # wrapper ← 不是 add!
print(add.__doc__) # None ← 文档也丢了
为什么会这样
@log 等价于 add = log(add),所以 add 这个名字指向的其实是 wrapper,元信息当然是 wrapper 的。
修复:functools.wraps
from functools import wraps
def log(func):
@wraps(func) # 把 __name__/__doc__/__module__ 等拷过来
def wrapper(*args, **kwargs):
print(f'call {func.__name__}')
return func(*args, **kwargs)
return wrapper
不加会有什么实际后果?
- Flask / FastAPI 路由函数名重复时报错(
wrapper撞名) help()、inspect.signature()拿到错误签名- 日志里打出来的函数名全是
wrapper,排查问题时完全没法定位
带参数的装饰器
多包一层:
def retry(times=3):
def deco(func):
@wraps(func)
def wrapper(*args, **kwargs):
for i in range(times):
try:
return func(*args, **kwargs)
except Exception:
if i == times - 1:
raise
return wrapper
return deco
@retry(times=5)
def call_api(): ...
记法:外层接装饰器参数,中层接函数,内层接函数调用参数。
楼主 · 2026-09-28 13:21 · 浏览 4

