查看全课大纲(第 18 / 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 如何快速统计元素出现次数?

while 循环的结束条件,你写对了吗?

约 12 分钟

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

在上一节我们学习了 while 循环的基本语法,知道了它会在条件为真时反复执行。本节我们通过课程里的杨辉三角形填空练习,把“循环什么时候停”这件事落到代码上:认清 5 个变量的分工,补全两处空白,并在循环末尾手动更新两个计数器。学完本节,你能独立检查一个嵌套 while 循环是否写对了结束条件。

💡 核心导读

  • 5 个变量:n、layer、values、new_values、index 各自负责什么。
  • 两处填空:内层循环遍历到哪个下标、打印哪个元素。
  • 边界的处理:遍历要覆盖最后一个元素,但“相邻两数相加”必须排除最后一个下标。
  • 手动自增:while 不会自动更新 layer 和 index,两处都要在循环末尾显式加 1。
  • 条件断点:用 IDE 的调试器在 layer == 3 时暂停,直接观察第三行的状态。

先认清程序里的 5 个变量

杨辉三角形的规则是:每一行两端都是 1,中间的每个数等于上一行“左上方”和“右上方”两个数之和。比如当前行 values 是 [1, 2, 1],下一行 new_values 就是 [1, 3, 3, 1]。

程序里的 5 个变量分工如下:

  • n:用户输入的三角形高度,也就是一共画几行。
  • layer:当前正在画第几层。
  • values:当前这一行的数字列表。
  • new_values:根据当前行算出来的下一行数字列表。
  • index:内层循环的索引,表示当前处理到这一行的第几个元素。

整段程序的思路是:外层循环控制画几行,内层循环一边打印当前行,一边把相邻两数相加、攒出下一行;一层画完,就把 new_values 交给 values,进入下一层。

两处填空:循环条件与打印的元素

课程里的练习留了两处空白。把骨架摊开看,空白分别在内层循环的条件和打印语句里:

n = int(input("请输入杨辉三角形的高度: "))   # 讲义演示时用固定值 4
layer = 1
values = [1]

while layer <= n:                 # 外层:一共画 n 行
    new_values = [1]             # 下一行的第一个 1
    index = 0
    while index < ____:          # 空 1:内层要遍历到哪个下标?
        print('%d ' % values[____], end='')   # 空 2:打印当前元素
        if index < len(values) - 1:
            new_values.append(values[index] + values[index + 1])
        index += 1
    new_values.append(1)         # 下一行最后一个 1
    values = new_values
    print('')                    # 换行
    layer += 1

空 1 填 len(values):index 从 0 开始,内层循环要让当前行的每一个元素都被处理到,所以条件是 index < len(values),index 的取值范围正好是 0 到 len(values) - 1。

空 2 填 index:打印的是 values 中下标为 index 的那个元素,也就是 values[index]。内层循环一进来就先打印当前元素,因此每一行的数字是在遍历过程中逐个输出的。

这里有一个容易写错的地方:内层循环条件不能写成 len(values) - 1。条件一旦减 1,最后一个元素就不会进入循环,打印也会跟着少一个;而“相邻两数相加”确实不能拿最后一个元素去加它后面的元素,所以边界要放在计算处,用 if index < len(values) - 1 单独判断。

把两处空白填好、高度取 4,完整代码如下:

# 原程序用 input() 读取高度,这里固定为 4 便于对照
n = 4
layer = 1
values = [1]

while layer <= n:
    new_values = [1]
    index = 0
    while index < len(values):
        print('%d ' % values[index], end='')
        if index < len(values) - 1:
            new_values.append(values[index] + values[index + 1])
        index += 1
    new_values.append(1)
    values = new_values
    print('')
    layer += 1
1 
1 1 
1 2 1 
1 3 3 1 

运行结果是 4 行:第 1 行只有 1;第 2 行由上一行的 1 补上两端得到 1 1;第 3 行中间是 1 + 1 = 2;第 4 行中间是 1 + 2 = 3 和 2 + 1 = 3。

循环末尾的两处自增

这段程序能停下来,靠的是循环体末尾两次显式自增:

  • index += 1:内层循环每处理完一个元素就右移一位,直到 index 等于 len(values),内层循环退出。
  • layer += 1:外层循环每画完一行就加 1,直到 layer 变成 n + 1,不再满足 layer <= n,外层循环退出。

while 和 for 不一样,它不会自动把计数器往前推。少了 index += 1,内层循环的条件永远成立;少了 layer += 1,外层循环会一直重复画同一行。这两个自增语句都必须在循环体的末尾真正执行到。

内层循环结束后还有两句收尾:new_values.append(1) 补上下一行最右边的 1;values = new_values 把刚算好的下一行变成下一轮的当前行。然后 print('') 输出一个换行,准备画下一行。

用条件断点观察第三层

如果只想看某一层的状态,不必每次都从头单步。可以在内层循环的打印语句处加一个断点,再把它设置成条件断点,条件写 layer == 3。这样程序只在画第三行时停下来。

停下时可以看到:layer 已经是 3,当前行 values 是 [1, 2, 1],而 new_values 还只有开头的一个 1。继续单步,就能看到 new_values 依次被加入 3、3,最后补上末尾的 1 变成 [1, 3, 3, 1]。对嵌套循环来说,条件断点比“每轮都停”更适合观察某一层的局部状态。

📝 动手练一练

把示例中的 n 从 4 改成 6 运行,写出新增的第 5 行和第 6 行。

👉 点击查看参考答案

思路:n 改成 6 后,外层循环多执行两轮;每一行仍然按“两端为 1、中间相邻两数相加”生成。

1 4 6 4 1
1 5 10 10 5 1

常见错误

错误写法现象或报错原因正确写法
内层条件写成 while index < len(values) - 1:,循环里还要打印当前元素每一行都少打印最后一个数字条件减 1 把最后一个下标排除在循环外,打印也跟着少一次遍历用 while index < len(values):,只在计算下一行时加 if index < len(values) - 1:
内层循环忘记写 index += 1程序卡住,内层循环退不出来index 一直不变,index < len(values) 永远成立在内层循环体末尾补上 index += 1
外层循环忘记写 layer += 1程序反复打印第一行,停不下来layer 一直不变,layer <= n 永远成立在外层循环体末尾补上 layer += 1
计算下一行时直接写 values[index] + values[index + 1],不做边界判断IndexError: list index out of rangeindex 到最后一个位置时,index + 1 已经越界先判断 if index < len(values) - 1:,再计算相邻两数之和

小结

  1. 杨辉三角形每一行两端是 1,中间的每个数等于上一行相邻两数之和。
  2. 外层循环用 layer <= n 控制行数,并在循环末尾用 layer += 1 让它最终退出。
  3. 内层循环用 index < len(values) 遍历并打印当前行的每个元素。
  4. “相邻两数相加”要排除最后一个下标,用 if index < len(values) - 1 保护,避免 IndexError。
  5. 内层循环结束后补上下一行末尾的 1,再用 values = new_values 进入下一轮。
  6. 嵌套循环里可以用条件断点(如 layer == 3)只观察某一层的变量状态。

📋 行动清单

  • 把示例中的 n 依次改成 6 和 10,运行并核对新增的每一行。
  • 尝试把外层 while 改写成 for layer in range(1, n + 1),比较两种写法的结束条件。
  • 检查自己写过的嵌套 while,确认内、外层循环变量都在循环体内被更新。

—— 小象教研组