logo
35

装饰器

⏱️ 35分钟

装饰器(Decorators):不改源码就增强函数能力

你现在可能会困惑什么

“为什么不直接改函数体,还要套一层?”

当你要给很多函数加相同能力(日志、鉴权、计时)时, 装饰器能减少重复代码并统一规范。

一句话定义

装饰器是接收函数并返回增强函数的高阶函数。

生活类比

像给手机加壳和贴膜: 本体不变,但能力增强。

最小可运行例子

from functools import wraps

def logger(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"[LOG] 调用: {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@logger
def greet(name):
    return f"Hello, {name}"

print(greet("JR"))

关键理解

  • @logger 等价于 greet = logger(greet)
  • wrapper 负责“增强行为 + 调原函数”
  • @wraps 保留原函数元信息(名称、doc)

课堂小测(5 分钟)

  1. 写一个打印函数名的装饰器。
  2. 改成打印函数参数。
  3. 实现 @timer 输出耗时(ms)。

课堂小测参考答案与判分点

  • 参考答案方向:能写出可运行代码,并覆盖题目中的核心条件与边界输入。
  • 判分点 1(正确性):主流程结果正确,关键分支可执行。
  • 判分点 2(可读性):变量命名清晰,结构不过度嵌套。
  • 判分点 3(健壮性):对空值、类型错误或异常输入有基础保护。

迁移任务(课后)

把两个重复写日志的函数改造成“一个装饰器 + 两个纯业务函数”。

本节验收标准

你能独立做到:

  • 写一个基础装饰器
  • 正确处理 *args, **kwargs
  • 解释装饰器如何替换原函数调用链

常见报错与调试步骤(新手版)

  • 报错看不懂:先读最后一行错误类型(如 TypeErrorNameError),再回到对应代码行定位。
  • 不确定变量值:在关键位置临时 print(变量, type(变量)),先确认数据是否符合预期。
  • 改了代码却没生效:确认文件已保存、运行的是当前文件、终端环境(venv)是否正确。

常见误区

  • 误区:忘记 return wrapper

  • 正解:不返回就不会完成替换。

  • 误区:不写 *args, **kwargs 导致参数丢失。

  • 正解:通用装饰器必须透传参数。