核心要点
理解装饰器本质是「接受函数返回函数」的高阶函数
会用 @syntax 和 functools.wraps 保留元信息
能举例带参数装饰器、类装饰器
简要回答
原理:装饰器是高阶函数——接收可调用对象,返回新的可调用对象
标准回答
原理:装饰器是高阶函数——接收可调用对象,返回新的可调用对象。
from functools import wraps
def log_calls(fn):
@wraps(fn) # 保留 __name__, __doc__
def wrapper(*args, **kwargs):
print(f"calling {fn.__name__}")
return fn(*args, **kwargs)
return wrapper
@log_calls
def train(epochs): ...
# 等价于 train = log_calls(train)常见场景:Flask/FastAPI 路由 @app.get、pytest @fixture、@lru_cache、@torch.no_grad()。
带参装饰器:三层嵌套 def deco(arg): def inner(fn): ... return wrapper; return inner。
类装饰器:实现 __call__ 的类也可作装饰器。
常见误区
⚠️ 常见踩坑
忘记 @functools.wraps,wrapper 会覆盖原函数的 name、doc,破坏内省与文档。还有人混淆「装饰器」与「带参数装饰器」:@deco 直接装饰,@deco(arg) 是先调用 deco(arg) 拿到真正的装饰器再装饰,少写一层嵌套就会报 TypeError。多个装饰器的应用顺序也常被记反——自下而上应用。
追问
追问 1:为什么需要 functools.wraps?
装饰后返回的是 wrapper,若不处理,fn.__name__ 会变成 'wrapper'、doc 丢失,影响日志、调试和文档工具(如 Sphinx、pytest)。@wraps(fn) 把原函数的 name、doc、wrapped 等元信息复制到 wrapper 上,保持透明。
追问 2:装饰器执行顺序是什么?
多个装饰器从下往上应用:@a @b def f 等价于 f = a(b(f)),先执行 b(f) 再 a(...)。调用时从最外层 wrapper 进入,像洋葱剥开一样逐层进入原函数。
追问 3:property 和装饰器有什么关系?
@property 把方法变成描述符,访问 obj.x 时调用 getter;@x.setter 定义赋值逻辑。是装饰器在描述符协议上的应用,用于封装属性访问。
延伸学习
与本题相关的知识库文章、术语、工具与行业资讯。
📰 AI 资讯