循环 — 让程序重复干活
循环 — 让程序重复干活
这一章学会让程序批量干活。 打印 1 到 5?写 5 行 print 太傻——循环五圈就行。 原书给两种循环各起了个准确的名字:while 是「按条件重复」,for 是「按次数重复」, 再配上 break、continue 两枚控制旋钮。这一章之后,「处理一千行数据」和 「打印一行字」对你来说是同一种活。
1. 这一章在全书链条里的位置
第 07 章让程序「看条件选路」,这一章让程序「按条件转圈」——两者合起来 才是完整的流程控制。原书点破循环的价值:把重复代码自动化,处理数据集里 的所有行、批量发信、掷一万次骰子,全是它1。第 05 章说 split 把字符串 切成列表——列表要怎么逐个处理?答案就是这一章的 for 循环。
2. 顶层全景:两条循环,一对旋钮
while 条件: 不知道转几圈,只知道什么时候停(条件为假即停)
for 项 in 序列: 知道转几圈,序列里有几项就转几圈
break 提前离场:整个循环立刻结束
continue 跳过本轮:剩下的话不说,直接进下一轮
图说:while 看「条件」,for 看「序列」;break 和 continue 两者通用。3. 核心原理一:while — 条件为真就一直转
骨架和 if 几乎一样:while 条件: 加缩进块2。差别在执行完块之后:
if 直接翻篇,while 会回到条件重新问一遍——真就再转一圈,假才离开2。
主线例子:i 从 0 到 5
原书的第一个演示,也是最好的 while 解剖标本3:
i = 0 # ① 计数器起步
while i < 5: # ② 条件:i 小于 5 吗
print(f'i is {i}, the condition {i}<5 is satisfied. Keep going!')
i += 1 # ③ 每圈结束,i 加 1
print('The loop has terminated.')逐圈回放:
第 1 圈前:i = 0,0 < 5 真 → 打印 "i is 0, ..." → i 变 1
第 2 圈前:i = 1,1 < 5 真 → 打印 "i is 1, ..." → i 变 2
第 3 圈前:i = 2,真 → 打印 "i is 2, ..." → i 变 3
第 4 圈前:i = 3,真 → 打印 "i is 3, ..." → i 变 4
第 5 圈前:i = 4,真 → 打印 "i is 4, ..." → i 变 5
第 6 圈前:i = 5,5 < 5 假 → 不进入,直接打印 "The loop has terminated."
图说:三件套缺一不可——计数器(i)、条件(i < 5)、推进(i += 1)。
少了推进,i 永远是 0,条件永远真:这就是传说中的死循环。三件套的名字值得记住:计数器变量(记圈数)、循环条件(用计数器表达)、 更新语句(每圈推进计数器)。while 循环的一切事故,都能翻译成这三件套里 缺了哪一件。
第二形态:标志变量(flag)当条件
演示二换了个写法:不问计数器,问一个 True/False 变量4:
flag = True
while flag:
print(f'The condition is {flag}. Keep going!')
num = float(input('Enter a number larger than 10: '))
flag = num > 10
if not flag:
print('Error: Number is not larger than 10.')输入 11、12、10.01 都继续,输入 10 时 num > 10 为 False,flag 倒下,
循环退出并打印错误提示4。「flag(标志变量)」是编程的通行词:一个
专门负责「该不该继续」的布尔变量。它把循环条件从「数圈数」换成
「事办成没办成」——后者才是真实程序的常态。
while 练习的四个范式
练习区四道题,各是一个可以带走的 while 范式5:
| 范式 | 题目 | 要点 |
|---|---|---|
| 区间递增 | 打印 -5 到 5 | 计数器从 -5 起步,count <= 5 兜住终点 |
| 用户定界 | 打印用户给的区间 | 上下界都问用户,边界含两端(<=) |
| 倍增逼近 | 翻倍直到超过一百万 | 输入 2,序列 2, 4, 8, …(输出里看到 16384 一路涨到 524288) |
| 口令循环 | 输入 Done 才停 / 密码一致才放行 | 循环条件就是停止口令的反面:while msg != 'Done' |
口令循环的密码版值得多看一眼——第 07 章的密码核对只能查一次,配上 while
就变成了「不对就重来」:两次输入不同,整个询问过程原地重播,直到一致
输出 You are all set5。「把一次性校验套进循环」是交互程序的基本功。
4. 核心原理二:for 与 range — 按次数转
for 的骨架:for item in sequence:——序列里有几项,块就执行几次,
每次 item 指向下一个元素6。「sequence(序列)」这个第 05 章埋下的词
在这里兑现:列表、字符串都是序列,for 对它们逐项点名。
但最常配套的序列是 range()——专造整数列的函数:
range(3) # 0, 1, 2(从零起,不含 3——左闭右开,和切片一个脾气)
range(0, 5) # 0, 1, 2, 3, 4
range(2, 5) # 2, 3, 4
range(2, 5, 2) # 2, 4(第三个数是步长,隔几个取一个)三组对照演示把规则钉死:range(3) 转三圈;for i in [0,1,2,3] 里
循环变量确实逐个取列表里的值;for i in [0,1,2,3]: print('Hello, world')
块里不用 i 也行——四圈照旧,循环变量只负责数圈,不参与干活7。
练习里的两种偶数写法
打印 20 以内偶数,原书给了两种解法8:
# 写法一:全部过一遍,if 过滤
for i in range(21):
if i % 2 == 0:
print(i)
# 写法二:range 带步长,直接只造偶数
for i in range(0, 21, 2):
print(i)输出相同,思路相反:写法一「全筛」,写法二「只取」。range 的步长参数
能省掉一层 if——这是 for 循环的第一个优化直觉。打印 100 内 5 的倍数、
打印用户区间内的整数(range(low, high+1),注意 high+1 才含右端)8,
都是同一招的变体。
5. while 与 for 的互译:同一个任务,两种写法
书里的练习设计藏了一个彩蛋:打印 -5 到 5、打印用户区间,两道题在 while 节 和 for 节各做了一遍9。并排放看,就是两种循环的标准互译:
# while 版:自己造计数器、自己写条件、自己推进
count = -5
while count <= 5:
print(count)
count += 1
# for 版:range 把三件套打包好了
for i in range(-5, 6):
print(i)输出一模一样,连空白都没有。互译的规律一张表:
| while 的三件手写零件 | for 里 range 的对应参数 |
|---|---|
count = -5(起步) | range 的第一个参数(起点) |
count <= 5(条件) | range 的第二个参数(终点,左闭右开所以写 6) |
count += 1(推进,最容易漏写的那行) | range 的第三个参数(步长,默认 1) |
for 本质上是「三件套被 range 预先打包」的 while——range 把起步、终止、推进 封进一个调用里,还顺带消灭了「忘了写推进」这个死循环来源。所以实战口径很简单: 数得清圈数用 for,圈数不清用 while;两者都能写时,for 更短、更安全、 更不会写漏推进。10
6. 核心原理三:break 与 continue — 中途离场与跳拍
两个语句都住在循环体里11:
break:整个循环立刻结束,流程跳到循环外;continue:本轮剩下的语句跳过,直接开始下一轮。
书里用「magic number( magic 数字)」猜数游戏做演示,值得完整走一遍, 因为它把 break、continue、嵌套 if、标志变量全套用上了12:
win = False
while not win:
magic = 5
x = input('Guess an integer in 0-10, "exit" to leave: ')
if x.upper() == 'EXIT':
print(f'Sorry to see you go!')
break # 用户喊退 → 整个游戏结束
if int(x) < 0 or int(x) > 10:
print(f'You are still far! Do it again')
continue # 超范围 → 跳过本轮,重新猜
elif int(x) != magic:
print(f'You are close! Do it again!')
continue # 没猜中 → 跳过本轮,重新猜
print(f'You got it! The magic number is {magic}!')
win = True # 猜中 → 标志放倒,循环自然退出原书演示输入 12 → 10 → 8 → 3 → 5,五轮的输出分别是「太远」「接近」 ×3、「猜中」12。注意退出循环的两条路:break 跳出去(用户退出), 条件失效走出去(猜中)——前者是「紧急出口」,后者是「正门」。 练习区的小题把两者对得更齐:1 到 20 里逢 5 跳过(continue 版打印 1–4, 6–9, …), 逢 5 就停(break 版只打印 1–4)——跳过一拍和提前散场,一字之差, 结果天壤之别13。
7. 案例走查:四个程序,四个循环模式
素数 — 嵌套循环首秀
规则:输入 n,打印 2 到 n 之间的所有素数14。
n = int(input('Enter an integer: '))
for i in range(2, n+1):
prime = True # 先假设 i 是素数
for j in range(2, i): # 内层:拿 2 到 i-1 挨个试除
if i % j == 0:
prime = False # 被整除,假设破产
break # 内层立刻收工(找到了一个因数就够了)
if prime:
print(i)这是全书第一处嵌套循环(循环套循环)——外层 i 挨个巡视候选数, 内层 j 对每个候选数做审判。内层的 break 是个精细设计:一旦抓到 一个因数就立即停审,后面的除数不用再试14。输入 20,输出 2 3 5 7 11 13 17 19。手验一下 9:外层 i=9,内层 j 先试 2(9%2=1 不中)、 试 3(9%3=0 中!)→ prime=False → break → 9 不打印 ✓。
成绩册 — 累加器与最值追踪
问学生人数,逐个问成绩,最后报平均分、最低分、最高分15:
n = int(input('Enter the number of students: '))
total = 0 # 累加器
min = 100 # 最低分追踪:先设成最高可能值
max = 0 # 最高分追踪:先设成最低可能值
for i in range(n):
grade = int(input('Enter the grade of the student: '))
total += grade
if grade < min:
min = grade
if grade > max:
max = grade
print(f'Average: {total/n:.2f}. Min: {min}. Max: {max}')输入 3 人,成绩 100、99、90,输出 Average: 96.33. Min: 90. Max:10015。
三个新武器一次收齐:累加器(total += grade,从零起步一圈圈攒)、
最值追踪(min 初始 100——任何真成绩都比它低,第一个成绩进来
必然刷新;max 同理)。这套「初始化 → 循环里更新 → 循环后使用」的
三拍结构,是数据处理程序的通用骨架。
(一个要留意的细节:变量名 min、max 撞了 Python 的内建函数名——
这是原书代码的原样,我们照抄。自己写时叫 min_grade 更稳。)
温度换算 — 循环里的 input
问华氏度、报摄氏度,直到输入 stop:while f != 'stop' 加循环体末尾
再要一次输入16。输入 32 → 98 → 24 → stop,输出 Celsius is 0、
Celsius is 37、Celsius is -416。
这个案例的教学点不在算式((f - 32) * 5 / 9,第 06 章案例的复用),
而在循环次数由用户口头决定——用户敲多少次 stop 前就算多少次,
这才是「不知道转几圈」的典型场景,while 的主场。
数字母 — for 循环遍历字符串
数一句里 e 和 E 各有多少个17:
sentence = input('Enter a sentence: ')
number_e = 0
number_E = 0
for c in sentence: # 字符串也是序列:逐个字符点名
if c == 'e':
number_e += 1
elif c == 'E':
number_E += 1
print(f'Number of e: {number_e}. Number of E: {number_E}.')输入 Example Sentence with EeeeEEEEE,输出 Number of e: 7. Number of E: 7.17。
for 循环逐个字符走一遍、分支各数各的——字符串当序列用,这是第 05 章
「序列是跨类型的通用机制」的又一次兑现。
8. 作者的判断与证据
- 「while 按条件、for 按次数」的二分是原书的组织主轴,也是 Python 社区的 标准心智模型26。两类循环理论上可以互相改写(for 能干的 while 都能干), 但各守各的主场:次数已知用 for,停时未知用 while。
- 嵌套循环用在素数上是个恰当的初次亮相:内层循环的循环次数依赖外层变量 i
(
range(2, i)),这是嵌套最难懂的形态,书选了最小的例子14。 - GenAI 互动区照例把正文没讲的全问了:死循环怎么防、为什么 break 有时 「不灵」、嵌套循环为什么慢18——前三节正文给的答案够用,见边界节。
9. 边界与局限
- 死循环(条件永远真的 while)只提了名字,没给系统的防法。 对策三件套: 确保循环体内有语句能改变条件所依赖的变量;打印循环变量观察它动不动; Jupyter 里直接停掉内核。原书演示的三件套结构(计数器+条件+推进)其实就是 防死循环的最佳实践3。
- for 遍历字典、集合的规则没讲(其实第 13、14 章才有对象),
enumerate() for遍历字典、集合的规则没讲(其实第 13、14 章才有对象),enumerate()(想要「序号+元素」两件套时的工具)也没出现——等你真需要时它们都在。 还有zip()(两列并排遍历)、列表推导式(一行造列表)这些 for 的 黄金搭档,本书同样留白——它们是让 Python 代码「pythonic」的主力, 值得在学完第 11 章后自己补一课。 GenAI 区问的「break 不灵」,多半是这个。想在多层里全身而退,得用标志变量 逐层传递退出信号18。- 嵌套循环的复杂度只在 GenAI 区问了一句没答18:两层各转 n 圈是 n² 次, 素数例子的内层 range(2, i) 让总量是 n²/2 量级——数据量一大就得换算法, 那是「算法」课的内容,本书到此为止。
- 循环的
else分支没讲。 Python 的循环可以挂 else:循环自然跑完 (没被 break 打断)才走 else,break 跳出的不走——冷门但实用的结构, 本书没出现,知道存在即可;搜资料时用「for else」作关键词。
10. 可带走的
- while 三件套:计数器(或标志变量)、条件、推进;缺推进即死循环, for 把三件套打包成 range 一次调用23。
- for 逐项点名序列;
range(起点, 终点, 步长),左闭右开,与切片同脾气—— 「终点写 high+1」和「切片 stop 不含」是同一条规矩的两次亮相67。 - range 步长能省一层 if(打印偶数的两写法)8。
- break 紧急出口,continue 跳一拍;退出循环有「跳出去」和「条件失效走出去」 两条路,猜数游戏里两条路各走一遍1112。
- 口令循环:
while msg != 'Done'——循环条件写成停止条件的反面; 密码重问是「一次性校验套进循环」的样板5。 - 素数两招:先假设后证伪(prime 标志变量),找到一个因数就 break 收工—— 「证伪一次就够、不必审完」是内层 break 的理由14。
- 累加器与最值追踪:初始化 → 循环更新 → 循环后使用15。
- 字符串能被 for 逐个字符遍历(for c in sentence)——「序列」的通用性 在列表之外再次兑现17。
- 嵌套循环的 break 只跳一层(内层收工、外层照旧);想在多层里全身而退, 用标志变量逐层传话18。
11. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 循环的价值与场景 | CHAPTER 8: Repetition | text/17-ch08-chapter-8-repetition.txt:5(搜「saves our time and effort」) |
| while 骨架与语义 | 同上 | text/17-ch08-chapter-8-repetition.txt:19(搜「basic structure of a while loop」) · text/17-ch08-chapter-8-repetition.txt:23(搜「checks this condition before each iteration」) |
| i<5 主线演示 | 同上 | text/17-ch08-chapter-8-repetition.txt:31(搜「Initialize a counter variable」) · text/17-ch08-chapter-8-repetition.txt:45(搜「i is 0」) |
| 标志变量演示 | 同上 | text/17-ch08-chapter-8-repetition.txt:52(搜「Initialize a flag variable」) · text/17-ch08-chapter-8-repetition.txt:74(搜「10.01」) |
| while 四练习 | 同上 | text/17-ch08-chapter-8-repetition.txt:80(搜「integers from –5」) · text/17-ch08-chapter-8-repetition.txt:106(搜「1000000」) · text/17-ch08-chapter-8-repetition.txt:122(搜「continue, enter」) · text/17-ch08-chapter-8-repetition.txt:140(搜「Enter a new password」) |
| for 骨架与 range 演示 | 同上 | text/17-ch08-chapter-8-repetition.txt:160(搜「basic structure of a for loop」) · text/17-ch08-chapter-8-repetition.txt:172(搜「for i in [0, 1, 2, 3]」) · text/17-ch08-chapter-8-repetition.txt:184(搜「range(3)」) |
| 两种偶数写法 | 同上 | text/17-ch08-chapter-8-repetition.txt:206(搜「even numbers within 20」) |
| break/continue 定义 | 同上 | text/17-ch08-chapter-8-repetition.txt:242(搜「allow you to skip」) |
| magic number 游戏 | 同上 | text/17-ch08-chapter-8-repetition.txt:250(搜「Initialize a win condition」) · text/17-ch08-chapter-8-repetition.txt:282(搜「Guess an integer」) |
| break 与 continue 对照练习 | 同上 | text/17-ch08-chapter-8-repetition.txt:294(搜「Check each integer in range(1, 20)」) · text/17-ch08-chapter-8-repetition.txt:302(搜「iteration immediately」) |
| 素数案例 | 同上 | text/17-ch08-chapter-8-repetition.txt:317(搜「Prime numbers」) · text/17-ch08-chapter-8-repetition.txt:329(搜「for j in range(2, i)」) |
| 成绩册案例 | 同上 | text/17-ch08-chapter-8-repetition.txt:342(搜「grade book」) · text/17-ch08-chapter-8-repetition.txt:371(搜「Average: {total/n」) |
| 温度换算案例 | 同上 | text/17-ch08-chapter-8-repetition.txt:379(搜「Fahrenheit to Celsius」) · text/17-ch08-chapter-8-repetition.txt:387(搜「while f != 'stop'」) |
| 数字母案例 | 同上 | text/17-ch08-chapter-8-repetition.txt:407(搜「How many E and e」) · text/17-ch08-chapter-8-repetition.txt:419(搜「for c in sentence」) |
Footnotes
-
出处:「CHAPTER 8: Repetition」第 3–8 段(
text/17-ch08-chapter-8-repetition.txt:7,搜「all rows in a dataset」;:8,搜「many times」)。应用场景三例逐字依据原文。 ↩ -
出处:「CHAPTER 8: Repetition」第 14–27 段(
text/17-ch08-chapter-8-repetition.txt:19,搜「basic structure of a while loop」;:23,搜「checks this condition before each iteration」;:25,搜「stops and the program continues」)。「执行完块之后回条件重问」是对原文流程的机制化转述。 ↩ ↩2 ↩3 ↩4 -
出处:「CHAPTER 8: Repetition」第 30–50 段(
text/17-ch08-chapter-8-repetition.txt:35,搜「at most 5 times」;:40,搜「i += 1」;:45–:50,逐圈输出;:50,搜「terminated」)。五圈输出与终止行逐字依据原文;「三件套」的命名是我们的归纳。 ↩ ↩2 ↩3 -
出处:「CHAPTER 8: Repetition」第 52–77 段(
text/17-ch08-chapter-8-repetition.txt:56,搜「while flag」;:64,搜「flag = num > 10」;:70–:77,输入输出序列)。flag 语义与四组输入输出逐字依据原文。 ↩ ↩2 -
出处:「CHAPTER 8: Repetition」第 79–151 段(
text/17-ch08-chapter-8-repetition.txt:81,搜「count = -5」;:105,搜「Enter a small positive number」;:123,搜「msg != 'Done'」;:142`,搜「password1 != password2」)。四题的代码与关键输出见原文;「口令循环条件写成停止条件的反面」是我们的概括。 ↩ ↩2 ↩3 -
出处:「CHAPTER 8: Repetition」第 155–167 段(
text/17-ch08-chapter-8-repetition.txt:156,搜「count-based repetition」;:160,搜「basic structure of a for loop」;:165,搜「list, tuple, string, or range」)。for 的序列来源四种见原文。 ↩ ↩2 ↩3 -
出处:「CHAPTER 8: Repetition」第 170–203 段(
text/17-ch08-chapter-8-repetition.txt:177,搜「print('Hello, world')」;:184,搜「range(3)」;:197,搜「range(2, 5)」;:202,搜「range(2, 5, 2)」)。四组 range 对照与「不用 i 也转圈」的演示见原文。 ↩ ↩2 -
出处:「CHAPTER 8: Repetition」第 205–238 段(
text/17-ch08-chapter-8-repetition.txt:206,搜「even numbers within 20」;:214,搜「dividable by 5」;:234`,搜「range(low, high+1)」)。两写法的对照与右端 +1 的技巧见原文。 ↩ ↩2 ↩3 -
出处:「CHAPTER 8: Repetition」第 80–84 段(
text/17-ch08-chapter-8-repetition.txt:81,搜「count = -5」)与第 221–223 段(text/17-ch08-chapter-8-repetition.txt:222,搜「range(-5, 6)」)。同一任务在 while 节与 for 节各出现一次,是我们对比两节练习区发现的。 ↩ -
「for 是三件套打包」是对两种循环机制的对照解读(我们的归纳);两写法的代码与输出见原文第 80–84、221–223 段。 ↩
-
出处:「CHAPTER 8: Repetition」第 240–247 段(
text/17-ch08-chapter-8-repetition.txt:243,搜「immediately exits」;:244,搜「continue statement skips」)。break/continue 定义逐字依据原文。 ↩ ↩2 -
出处:「CHAPTER 8: Repetition」第 249–291 段(
text/17-ch08-chapter-8-repetition.txt:254,搜「while not win」;:265,搜「break」;:271,搜「continue」;:282–:291,五轮游戏输入输出)。演示序列(12→10→8→3→5)逐字依据原文。 ↩ ↩2 ↩3 -
出处:「CHAPTER 8: Repetition」第 293–306 段(
text/17-ch08-chapter-8-repetition.txt:297,搜「i %5 == 0」;:305,搜「break」)。continue 版打印 1–4、6–9…,break 版只打印 1–4——输出差异由代码结构直接推得(书中此练习未印输出,判断依据代码,标为我们的验算)。 ↩ -
出处:「CHAPTER 8: Repetition」第 317–341 段(
text/17-ch08-chapter-8-repetition.txt:324,搜「for i in range(2, n+1)」;:329,搜「for j in range(2, i)」;:333,搜「Break out of the inner loop」)。嵌套结构与内层 break 的注释见原文;输入 20 的输出(2 3 5 7 11 13 17 19)按代码推得,9 的手验是我们的验算。 ↩ ↩2 ↩3 ↩4 -
出处:「CHAPTER 8: Repetition」第 342–377 段(
text/17-ch08-chapter-8-repetition.txt:350,搜「Initialize total as 0」;:351,搜「Initialize min with a high value」;:360,搜「total += grade」;:373–:377,输入 3 人成绩 100/99/90 及输出)。代码与输出逐字依据原文;「min/max 撞内建函数名」是我们对代码的观察。 ↩ ↩2 ↩3 -
出处:「CHAPTER 8: Repetition」第 379–405 段(
text/17-ch08-chapter-8-repetition.txt:387,搜「while f != 'stop'」;:399–:405,输入 32/98/24/stop 及输出)。四组换算输出逐字依据原文。 ↩ ↩2 -
出处:「CHAPTER 8: Repetition」第 407–431 段(
text/17-ch08-chapter-8-repetition.txt:419,搜「for c in sentence」;:430,搜「Example Sentence with EeeeEEEEE」;:431,搜「Number of e: 7」)。输入输出逐字依据原文。 ↩ ↩2 ↩3 -
出处:「CHAPTER 8: Repetition」第 433–450 段(
text/17-ch08-chapter-8-repetition.txt:440,搜「infinite loops」;:446,搜「not exit a loop as expected」;:450,搜「nested loops becoming too complex」)。三问均由 GenAI 互动区提出,正文未答,我们的回答写在 §8。 ↩ ↩2 ↩3 ↩4