查看全课大纲(第 37 / 51 节)
  1. 1.Python 是什么?为什么值得学?
  2. 2.如何为 Python 编程准备一个“全家桶”环境?
  3. 3.Python 有哪几种执行方法?
  4. 4.行与缩进到底有什么用?
  5. 5.注释有什么用,怎么写?
  6. 6.变量:如何给数据起个好名字?
  7. 7.运算符的优先级和常见坑点是什么?
  8. 8.Python 中,数字和布尔值究竟有哪几种?
  9. 9.输入函数 `input()` 和文件读取 `open()` 怎么用?
  10. 10.如何将程序的结果展示给用户或保存下来?
  11. 11.CSV 文件怎么读怎么写才方便?
  12. 12.列表是什么,如何创建和访问它?
  13. 13.列表的修改操作,有哪些方法?
  14. 14.列表切片为什么能取到部分元素?排序和查找又该怎么用?
  15. 15.判断语句:如何让程序根据条件做不同的事?
  16. 16.for 循环如何遍历序列中的元素?
  17. 17.while 循环什么时候才会停?
  18. 18.while 循环的结束条件,你写对了吗?
  19. 19.循环中途想提前结束,怎么办?
  20. 20.`break` 和 `continue` 到底有什么区别?
  21. 21.列表推导式如何让代码更简洁高效?
  22. 22.字符串怎么定义、拼接和操作?
  23. 23.元组:为什么定义单个元素时要加逗号?
  24. 24.集合(set)怎么用?如何快速去重和做集合运算?
  25. 25.字典是什么,为什么比列表更方便?
  26. 26.字典的增删改查与遍历,如何避免 KeyError?
  27. 27.函数里的变量,为什么有的能用有的不能用?
  28. 28.在函数内部修改全局变量,为什么有时不生效?
  29. 29.函数里能修改全局变量吗?返回值怎么用?
  30. 30.匿名函数:如何用 lambda 快速定义简单函数?
  31. 31.面向对象编程是什么,为什么需要它?
  32. 32.类中的方法和属性到底怎么定义和使用?
  33. 33.如何控制对对象内部属性和方法的访问?
  34. 34.异常发生时,程序会怎样继续执行?
  35. 35.如何组织代码:包和模块
  36. 36.`if __name__ == '__main__'` 到底有什么用?
  37. 37.如何用 IDE 的 Debug 功能排查程序错误?
  38. 38.Matplotlib 绘图的第一步该做什么?
  39. 39.Figure 是什么,如何设置图形尺寸?
  40. 40.如何自定义 Matplotlib 的坐标轴?
  41. 41.如何为图表添加图例,让不同曲线一目了然?
  42. 42.饼图怎么画才能直观展示数据占比?
  43. 43.如何用 Matplotlib 绘制散点图?
  44. 44.柱状图怎么画?
  45. 45.如何使用 Matplotlib 绘制等高线图?
  46. 46.如何在 Matplotlib 中绘制子图?
  47. 47.zip 函数如何把多个列表“打包”在一起?
  48. 48.如何给可迭代对象加上索引?
  49. 49.random 模块如何生成随机数?
  50. 50.math 模块怎么用?
  51. 51.Counter 如何快速统计元素出现次数?

如何用 IDE 的 Debug 功能排查程序错误?

约 10 分钟

小象实战讲义 · Python 编程从入门到实践

在编写 Python 程序时,代码的实际运行结果有时会与预期不符。当遇到这种情况,我们需要一种方法来定位问题所在。本节将介绍如何使用集成开发环境(IDE)的 Debug(调试)功能,通过设置断点、单步执行和观察变量来排查代码中的逻辑错误。这是继学习控制流和数据结构后,提升代码排查能力的重要实践技能。

💡 核心导读

  • Debug 是什么:一种通过控制程序执行流程、观察运行状态来定位错误的方法。
  • 断点的作用:让程序在指定位置暂停,以便检查当时的变量值和执行路径。
  • 条件断点:当满足特定条件(如某个变量等于特定值)时,程序才会在断点处暂停。
  • 单步执行与变量观察:在程序暂停后,可以逐行执行代码,并实时查看变量值的变化。
  • Debug 的基本流程:设置断点 → 启动调试 → 触发暂停 → 观察分析 → 修复问题。

Debug 的基本概念与流程

当程序运行结果不符合预期时,我们通常需要检查代码在执行过程中是否按我们设想的逻辑运行。Debug(调试)就是帮助我们完成这个检查的工具。

在 IDE 中进行 Debug 的核心是 设置断点(Breakpoint)。断点是代码中的一个标记,程序运行到此处时会自动暂停,等待开发者检查。

一个典型的 Debug 流程包含以下几步:

  1. 设置断点:在怀疑有问题的代码行左侧双击(或在相应菜单中操作),通常会显示一个红点。
  2. 启动调试:在 IDE 中选择“Debug”或“调试”模式运行程序,而不是普通的“Run”。
  3. 程序暂停:当程序执行到断点所在行时,会自动暂停,此时该行代码尚未执行。
  4. 观察与分析:在程序暂停的状态下,你可以:
    • 查看所有变量的当前值。
    • 使用“单步执行”(Step)按钮,让程序一次只执行一行代码,观察执行流程。
    • 检查控制台输出,判断程序逻辑。
  5. 发现问题并修复:通过对比观察到的变量值和预期值,定位逻辑错误,然后修改代码。

一个 Debug 实战:排查杨辉三角程序错误

我们通过一个具体的例子来演示 Debug 过程。以下是一个绘制杨辉三角的程序,但在高度为 4 时,最后一行输出错误(正确应为 1 3 3 1,程序输出为 1 2 4 1)。

# 有错误的杨辉三角生成程序(用于演示Debug)
n = 4  # 原程序使用 input() 输入三角形高度,此处为演示固定为4
print("高度为 {} 的杨辉三角(当前有错误):".format(n))

layer = 1
values = [1]  # 存储当前行的值

while layer <= n:
    # 打印当前行
    for num in values:
        print(num, end=' ')
    print()

    # 计算下一行
    next_values = [1]  # 下一行第一个元素总是1
    index = 0
    while index < len(values) - 1:
        # 计算下一行中间的元素:应为当前行相邻两元素之和
        next_element = values[index] + values[index]  # 这里有错误!
        next_values.append(next_element)
        index += 1
    next_values.append(1)  # 下一行最后一个元素总是1

    # 为下一轮循环准备
    values = next_values
    layer += 1
高度为 4 的杨辉三角(当前有错误):
1 
1 1 
1 2 1 
1 2 4 1 

可以看到,第四行输出是 1 2 4 1,这显然不是杨辉三角(第四行应为 1 3 3 1)。问题出在计算下一行元素的逻辑上。

设置并利用条件断点

我们怀疑问题出现在计算第三行(layer = 3)并推导第四行数值的时候。我们可以在外层 while 循环开始处设置一个条件断点,让它只在 layer == 3 时暂停。

在 Spyder 中的操作通常如下:

  1. 在代码行 while layer <= n: 左侧双击,设置一个普通断点(红点)。
  2. 在 Debug 菜单中找到“断点列表”或类似功能。
  3. 找到该断点,并设置条件(Condition)为 layer == 3。
  4. 以 Debug 模式运行程序。

程序会在准备开始绘制第三行时暂停。此时,我们可以打开“变量查看器”(Variable Explorer)看到当前 layer 的值为 3,values 是 [1, 2, 1](即第三行的正确值)。

单步执行与观察变量

程序暂停后,我们使用“单步执行”(Step Into/Over)功能,逐行运行代码。

  1. 第一次单步,会进入 while layer <= n: 循环体。
  2. 继续单步,会执行打印当前行(第三行)的循环,控制台输出 1 2 1。
  3. 关键步骤:当执行到计算 next_element 的行时,观察右侧变量或表达式求值窗口。
    • 此时 index 为 0。
    • values[index] 是 1。
    • 错误的代码是 values[index] + values[index],结果计算为 1 + 1 = 2。
    • 而正确的逻辑应该是 values[index] + values[index + 1],即 1 + 2 = 3。

通过单步执行和观察,我们精准地定位了错误:在计算下一行元素时,错误地将同一个元素相加,而不是相邻的两个元素相加。

修复错误并验证

找到错误后,停止 Debug 进程,修改代码:

# 错误代码:
next_element = values[index] + values[index]  # 这里有错误!

# 正确代码:
next_element = values[index] + values[index + 1]  # 应取相邻两元素之和

修改后,重新运行程序:

# 修复错误后的杨辉三角生成程序
n = 4
print("高度为 {} 的杨辉三角(已修复):".format(n))

layer = 1
values = [1]

while layer <= n:
    for num in values:
        print(num, end=' ')
    print()

    next_values = [1]
    index = 0
    while index < len(values) - 1:  # 保证 index+1 不越界
        next_element = values[index] + values[index + 1]  # 已修复
        next_values.append(next_element)
        index += 1
    next_values.append(1)

    values = next_values
    layer += 1
高度为 4 的杨辉三角(已修复):
1 
1 1 
1 2 1 
1 3 3 1 

程序输出了正确的结果。修复时只改了 next_element 的计算:把 values[index] + values[index] 改成 values[index] + values[index + 1],取当前行相邻的两个元素相加。

📝 动手练一练

  1. 把本节修复后的杨辉三角程序复制到 IDE:在 next_values.append(next_element) 这一行设置断点,以 Debug 模式运行;当 layer == 3 时逐行单步执行,观察 values 和 next_element 的变化,并回答:第 4 行(1 3 3 1)的中间两个 3 分别由哪两个数相加得到?
👉 点击查看参考答案

思路:用条件断点把程序停在 layer == 3(也就是正在生成第 4 行)的时候,再逐行单步执行,在变量查看器里观察 values(第 3 行 [1, 2, 1])以及每次 next_element 的取值。

参考答案:

第 3 行为 [1, 2, 1];生成第 4 行时:

  • 第一个 3 = values[0] + values[1] = 1 + 2;
  • 第二个 3 = values[1] + values[2] = 2 + 1;
  • 再加上首尾的两个 1,得到第 4 行 1 3 3 1。

常见错误

错误写法 / 操作现象或报错原因正确写法 / 操作
在 Debug 时,期望程序在修改代码后立即反映修改代码后,继续执行(Resume),程序行为未变,甚至报错。Debug 会话启动后,运行的是当时加载到内存的代码映像。修改磁盘上的源文件不会自动更新内存中的程序。停止(Stop)当前的 Debug 会话,重新以 Debug 模式运行程序,加载最新的代码。
设置断点后直接点“Run”而不是“Debug”程序完全运行,没有在断点处暂停。普通运行模式会忽略断点。只有调试模式才会激活断点功能。在 IDE 中明确选择“Debug”按钮或菜单启动程序。
在循环开始处设无条件断点,用于检查特定迭代程序在每一次循环迭代时都暂停,需要手动继续很多次才能到达关心的那次迭代。无条件断点会在每次执行到该行时都触发暂停。使用条件断点,设置只在满足特定条件(如 layer == 3)时才暂停。

小结

  1. Debug 是定位和修复代码逻辑错误的核心技能,通过控制执行流程和观察程序状态来实现。
  2. 断点是 Debug 的基石,程序执行到断点处会暂停。条件断点可以只在特定场景下触发,提高调试效率。
  3. 程序暂停后,利用单步执行可以精确控制代码运行,结合变量查看器可以实时观察数据变化,从而判断逻辑是否正确。
  4. Debug 的一般流程是:复现问题 → 根据逻辑推测可疑代码段 → 设置断点(尤其是条件断点) → 启动调试 → 单步执行并观察 → 定位问题 → 修复代码 → 重新验证。
  5. 发现并修复问题后,需要停止当前调试会话,再重新运行程序以验证修复是否成功。

📋 行动清单

  • 在 IDE 中打开一个已有的 Python 文件,尝试在任意一行设置一个普通断点,并分别用“Run”和“Debug”模式运行,观察区别。
  • 找一个包含循环的小程序,在循环体内设置一个条件断点(例如 i == 5),启动调试并观察程序是否在满足条件时才暂停。
  • 使用单步执行功能,逐行运行一个简单的函数,同时在变量窗口中观察主要变量的值是如何随着每一行代码的执行而变化的。

—— 小象教研组