核心要点

  • 理解装饰器本质是「接受函数返回函数」的高阶函数

  • 会用 @syntax 和 functools.wraps 保留元信息

  • 能举例带参数装饰器、类装饰器

简要回答

装饰器在不改原函数源码的前提下增强行为(日志、计时、鉴权、缓存);@decorator 等价于 func = decorator(func)。

标准回答

一、原理

装饰器是高阶函数——接收可调用对象,返回新的可调用对象。
示例代码可以这样理解:
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

这里要提醒面试官你知道一个常见坑:如果装饰器内部不使用 functools.wraps,原函数的名字、文档和签名会被 wrapper 覆盖,影响日志、调试、接口文档和测试框架识别。讲到这里,比只说“装饰器是语法糖”更像写过框架代码。

四、类装饰器

实现 __call__ 的类也可作装饰器。

回答思路

  • 【定义】用一句话说清「Python 装饰器」

  • 【原理】讲清关键机制或步骤(2~3 点)

  • 【例子】举一个真实项目、论文或产品中的例子

  • 【对比】与易混淆概念或替代方案比较(如有)

  • 【收尾】总结适用场景 + 一个局限或风险

延伸学习

PEP 318 引入函数装饰器;框架源码中大量装饰器模式值得阅读。

常见误区

⚠️ 常见踩坑

误区一:容易答偏的地方:只罗列名词、不会举例;混淆相似概念;忽略工程约束(成本、延迟、数据质量)。不确定时应主动说明假设。

追问

追问 1为什么需要 functools.wraps?

裸 wrapper 会把原函数的 __name____doc____module__ 覆盖成 wrapper 的,影响调试、文档生成和 inspect@wraps(fn) 把这些元数据复制回 wrapper。

追问 2装饰器执行顺序是什么?

多个装饰器从下往上应用:@a @b def f 等价于 f = a(b(f)),先执行 b(f)a(...)。调用时从最外层 wrapper 进入。

追问 3property 和装饰器有什么关系?

@property 把方法变成描述符,访问 obj.x 时调用 getter;@x.setter 定义赋值逻辑。是装饰器在描述符协议上的应用,用于封装属性访问。

🔗 相似问题

同一考点的不同问法,换着练更稳

延伸学习

按主题分类的相关资源,便于系统复习