这套课程按知识依赖排列。本篇的前置知识是:第 22、23、27 篇。学完后,你应该能够理解函数被包装后的调用顺序。
本篇要解决的问题
本篇只处理“装饰器如何包装函数”这一件事。示例基于 Python 3.14.6,在 macOS 环境核验;除明确标注的终端命令外,代码本身只使用 Python 标准能力。
先看清一个容易误解的细节
装饰器接收原函数并返回包装函数。名称重新绑定后,调用看似仍是原函数名,实际会先进入包装层,再决定何时调用原函数。
核心概念
@trace等价于add = trace(add)。- 包装函数必须返回原函数结果,否则调用语义会被改变。
functools.wraps保留原函数名称和文档信息。
跟着示例运行
把下面代码保存到本课脚本并运行:
from functools import wraps
def trace(function):
@wraps(function)
def wrapper(*args, **kwargs):
print("开始:", function.__name__)
result = function(*args, **kwargs)
print("结束:", function.__name__)
return result
return wrapper
@trace
def add(left, right):
return left + right
print(add(2, 3))
预期结果:
开始: add
结束: add
5
输出中包含环境路径、集合元素或日志格式时,具体文字和顺序可能随本机环境变化;判断是否成功应以本篇说明的关键结果为准。
按执行顺序拆开看
- 解释器执行定义时,把原
add函数交给trace,返回的wrapper重新绑定到名字add。 - 调用时先进入包装函数,包装函数再调用原函数、取得结果并原样返回。
再看一个具体细节
装饰器语法看起来特殊,但可以还原成普通赋值。
@trace 位于函数定义上方,效果等价于定义结束后执行 add = trace(add)。
初学者容易混淆的地方
- 包装函数忘记返回原函数结果,调用方得到
None。 - 装饰器叠加过多且修改隐藏行为,使调用路径难以追踪;基础项目优先保持层数少。
练习
- 编写只记录调用次数的简单装饰器。
- 删除包装函数的
return result,观察调用方得到什么。
练习应先独立完成,再通过增加 print 输出或检查文件内容观察中间状态。当前课程尚未讲到的能力,不要求提前搜索复杂写法。
本篇边界
本篇目标是理解函数被包装后的调用顺序。没有展开的高级机制会放到后续课程;先确保能够解释示例中每一行代码,再进入下一篇。
相关文章
01|Python 程序从源码到输出经历了什么
面向按顺序学习 Python 的初学者,本篇在零基础上,帮助读者理解源代码、解释器和输出之间的关系。
02|在 macOS 安装并确认 Python 3 环境
面向按顺序学习 Python 的初学者,本篇在第 01 篇知识上,帮助读者在 macOS 中确认 Python 3.14 和 pip 是否可用。
03|交互式解释器和 Python 脚本怎么选择
面向按顺序学习 Python 的初学者,本篇在第 02 篇知识上,帮助读者根据任务选择 REPL 或 `.py` 脚本。
04|建立第一个 Python 项目目录
面向按顺序学习 Python 的初学者,本篇在第 03 篇知识上,帮助读者理解当前目录并正确运行项目脚本。

