4.4 调试器综合练习
前置知识:本章前三页
预计时间:20 分钟
练习环境:Python 3 与已配置好的调试器
完成标志:能用栈帧解释递归,并用自动测试验收
这页包含一个需要修复的边界错误,以及一段用于观察调用栈的正确递归程序。先普通运行,记录失败或成功的实际结果,再进入调试器;修改后用新进程重新验证。
练习一:边界条件
复制基础页的材料筛选程序,先用 python3 material_checker.py 确认断言失败,再在循环里的 if 条件设置断点。观察零重量项,修复 > 为 >=,退出调试模式后重新运行脚本。
练习二:递归调用栈
python
def factorial(n):
if isinstance(n, bool) or not isinstance(n, int):
raise TypeError("n must be an integer")
if n < 0:
raise ValueError("n must be non-negative")
if n == 0:
return 1
return n * factorial(n - 1)
assert factorial(0) == 1
assert factorial(5) == 120保存为 factorial_debug.py,运行 python3 factorial_debug.py 应正常退出且没有输出。这里显式拒绝布尔值,因为 Python 的 bool 是 int 的子类,而本例只接受整数输入。递归还受解释器栈深度限制,这个练习不适合计算任意大的阶乘。
在递归行暂停:
- Step Into,观察调用栈增加一帧。
- 在条件
n == 2时暂停,核对当前帧与调用者帧的n。 - Step Out,观察当前调用返回。
- 不要在 Watch 中再次调用
factorial,否则会产生新的递归执行。 - 最后不用调试器运行两个断言。
排障顺序
- 普通运行是否仍复现?
- 是否启动了正确文件、测试或服务进程?
- 断点是否绑定到可执行行?
- 解释器、虚拟环境、容器和源码映射是否一致?
- 条件是否从未为真或求值失败?
- 优化、异步或线程切换是否改变了暂停位置?
日志断点、条件断点和调试控制台都可能产生开销或副作用。不要用调试环境中的一次现象代替可重复测试。本页的 assert 只用于保持示例最小;真实项目应把这些边界放进测试框架,并注意 Python 在 -O 模式下会移除 assert。
验收
- 能说出 Continue、Step Over、Step Into、Step Out 的差别。
- 能从当前栈帧向上找到调用者,并解释参数来自哪里。
- 知道 Watch 表达式可能执行代码。
- 修复后能在新进程中通过断言。
完成后,你应保留边界错误修复前后的结果,并能解释递归各帧中的 n。下一章开始处理项目依赖、版本选择和可重复构建。