跳到内容

第 0 卷 / 第 4 章 / 第 4 课

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 的子类,而本例只接受整数输入。递归还受解释器栈深度限制,这个练习不适合计算任意大的阶乘。

在递归行暂停:

  1. Step Into,观察调用栈增加一帧。
  2. 在条件 n == 2 时暂停,核对当前帧与调用者帧的 n。
  3. Step Out,观察当前调用返回。
  4. 不要在 Watch 中再次调用 factorial,否则会产生新的递归执行。
  5. 最后不用调试器运行两个断言。

排障顺序 ​

  1. 普通运行是否仍复现?
  2. 是否启动了正确文件、测试或服务进程?
  3. 断点是否绑定到可执行行?
  4. 解释器、虚拟环境、容器和源码映射是否一致?
  5. 条件是否从未为真或求值失败?
  6. 优化、异步或线程切换是否改变了暂停位置?

日志断点、条件断点和调试控制台都可能产生开销或副作用。不要用调试环境中的一次现象代替可重复测试。本页的 assert 只用于保持示例最小;真实项目应把这些边界放进测试框架,并注意 Python 在 -O 模式下会移除 assert。

验收 ​

  • 能说出 Continue、Step Over、Step Into、Step Out 的差别。
  • 能从当前栈帧向上找到调用者,并解释参数来自哪里。
  • 知道 Watch 表达式可能执行代码。
  • 修复后能在新进程中通过断言。

完成后,你应保留边界错误修复前后的结果,并能解释递归各帧中的 n。下一章开始处理项目依赖、版本选择和可重复构建。

→ 下一步:为什么需要包管理器

Built with VitePress | Software Systems Atlas