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