PY-02 流程控制
02 · 流程控制(Control Flow)
📅 预计 60 分钟 | ⭐ = 高频考点
✍️ 本讲配套练习:本地python grader.py 02,或网页练习
2.1 条件语句 if ⭐
想象你站在一个岔路口:天气好就走小路,下雨就走大路,天黑了干脆回家。程序要「看情况走不同的路」,靠的就是条件语句。Python 的 if 语法核心只有两个东西:冒号 + 缩进。冒号告诉 Python「下面是被这个条件管住的代码」,缩进告诉 Python「哪些行归这个条件管」。
score = 85
if score >= 90:
print("优秀") # 只有 >=90 才打印
elif score >= 60: # else if 的缩写
print("及格") # 第一个为真的分支才会执行
else:
print("不及格")
# 输入: score=85 → 输出: 及格
规则三句话记住:
if/elif/else后面跟条件表达式,结果必须是布尔值(或能转成布尔值的值)。- 一个
if可以有多个elif,但只会从上到下执行第一个为真的分支,后面的分支全部跳过。有点像自动售货机:投了 5 元,先看能不能买可乐,能就出可乐,绝不再问你要不要雪碧。 else可以省略;elif必须跟在if之后,不能单独出现。
条件表达式除了比较大小,还能做逻辑组合和成员判断:
x = 5
age = 20
has_id = True
is_rain = False
name = "小明"
scores = {"小明": 90}
# 数值判断
if x > 0: ... # 大于
if x == 0: ... # 相等——注意是 ==,不是 =
# 逻辑组合
if age >= 18 and has_id: ... # 且:两个都满足才通过
if x < 0 or x > 100: ... # 或:满足一个就通过
if not is_rain: ... # 非:取反
# 判断是否在容器里
if name in ["小明", "小红"]: ... # in 运算符 ⭐
if key in scores: ... # 字典里有没有这个键
if ch in "aeiou": ... # 字符串里有没有这个字符
⚠️ 常见错误
if x = 5:=是赋值,条件里要判断相等必须写==。这一处写错,程序不报错但行为完全不对,是改错题最爱出的一处。- 漏掉行尾冒号:
if x > 0少写:,直接SyntaxError,程序跑不起来。 - 缩进混用 Tab 和空格:PEP 8 规定用 4 个空格,混用会报
TabError: inconsistent use of tabs。 elif写成else if:那是 C 语言的写法,Python 里没有else if这个关键字。
💡 记忆口诀
「if 和 elif 是单选题,从上往下选第一个真的;else 是兜底,谁都不选就选它。」
2.2 for 循环 + range() ⭐
for 循环像是工厂流水线:传送带把一件件货物送过来,你一件件处理,直到全部处理完。在 Python 里,for 用来遍历一个序列(列表、字符串、range…),把每个元素轮流取出来用。
for i in range(5): # 只给一个数 = 从 0 开始
print(i, end=" ") # 输入: range(5) → 输出: 0 1 2 3 4
range() 是 Python 生成数字序列的函数,三种写法对应三种含义:
range(5) # 0,1,2,3,4 从 0 开始,到 5 前停
range(1, 6) # 1,2,3,4,5 指定起点和终点
range(0, 10, 2) # 0,2,4,6,8 第三个参数是步长 step
# 输入: 上面三行 → 输出: 各自注释里的数字序列
⭐ 全讲最重要的一个坑:range(start, stop, step) 是左闭右开——start 包含,stop 不包含。range(1, 5) 是 14,不是 15;想取到 5,得写 range(1, 6)。这一条几乎每次考试都考。
遍历列表和字符串,直接 for 过去就行:
names = ["小明", "小红", "小刚"]
for name in names:
print(name) # 输出: 小明 / 小红 / 小刚
for ch in "Python":
print(ch) # 输入: "Python" → 输出: P y t h o n(每个字母一行)
如果既想要元素,又想知道它在第几个位置,用 enumerate() ⭐:
for i, name in enumerate(names): # i 从 0 开始
print(i, name)
# 输出:
# 0 小明
# 1 小红
# 2 小刚
enumerate 的中文是「枚举」:一边数数,一边取值,一次性把索引和元素都给你。
⚠️ 常见错误
- 混淆
range(1, 5)和range(1, 6):左闭右开,stop这个数本身不进循环。 range(5)以为是从 1 到 5——其实从 0 到 4。很多同学打印出来看到 0 很惊讶。enumerate(names)忘了写i,:写成for name in enumerate(names),拿到的是(0, '小明')这样的元组,不是名字本身。- 遍历列表时顺手改列表(比如边遍历边删)容易出错,初学阶段先只读不改。
💡 记忆口诀
「range 像半开的门:进门(左闭)欢迎,出门(右开)再见;步长就像跨台阶,一格两格随意跨。」
2.3 while 循环
while 是「条件满足就一直做」的循环,更像一台 ATM 取款机:只要账户里还有钱(条件为真),就能继续取。它不关心「取多少次」,只关心「现在还能不能取」。
n = 1
while n <= 5:
print(n) # 输出: 1 2 3 4 5
n += 1 # 千万别忘了让条件最终为假!
条件判断在每轮循环开始时做:为真就进循环体,为假就退出。所以 while 的循环体里必须有一句让条件逐渐变假的代码(上面的 n += 1),否则条件永远为真,就成了死循环。
while 特别适合「不知道要循环多少次」的场景——比如不停地取数字的每一位,直到取完:
# 求一个整数各位数字之和(例如 123 → 1+2+3 = 6)
n = 123
total = 0
while n > 0:
total += n % 10 # 取出最后一位,累加
n //= 10 # 去掉最后一位
print(total) # 输入: n=123 → 输出: 6
这里的两个运算符是本节重头戏 ⭐:
%取余:123 % 10得 3,取出个位。//整除:123 // 10得 12,把个位抹掉。
两者配合,就能一位一位地把数字「剥开」,这叫拆位。
如果确实想让循环永远跑、直到某个条件从内部退出,就用 while True:,靠内部的 break 跳出(见 2.4 节)。注意:while True 本身是个「无限循环」,全靠 break 兜底,写不好就是死循环。
⚠️ 常见错误
- 忘记更新循环变量:
while n <= 5:里漏写n += 1,n 永远是 1,死循环。跑 grader 时如果某题「超时」,多半就是死循环。 - 边界写反:拆位用
while n > 0写成了while n >= 0,而 n 最终会变成 0,条件永远为真,死循环。 %和//方向搞反:n % 10是取个位,n // 10是去个位,两个都记成除就乱了。
💡 记忆口诀
「for 管次数(已知循环几遍),while 管条件(未知何时停);while 循环像上发条,每圈记得松一扣。」
2.4 break / continue ⭐
循环跑着跑着,可能需要提前退场,或者跳过某一次。这两个关键词就是干这个的:
break:立刻跳出整个循环,后面的循环轮次统统不执行了。continue:跳过本次循环的剩余代码,直接进入下一轮。
打个比方:你在公交车上报站。break 是「司机到站直接收车下班,后面站都不去了」;continue 是「这一站没停好,直接滑到下一站,但线路还要继续跑」。
for i in range(1, 10):
if i == 3:
continue # 跳过 3,不打印
if i == 7:
break # 到 7 就彻底结束循环
print(i) # 输出: 1 2 4 5 6
break 最经典的用法:在循环里找第一个符合条件的对象,找到就立刻收工,不用遍历完整个序列:
nums = [3, 8, 14, 21, 30]
for x in nums:
if x % 7 == 0:
print("第一个能被 7 整除的数:", x) # 输出: 第一个能被 7 整除的数: 14
break # 找到就退出,不再往后看
continue 则常用于「把不符合条件的元素过滤掉」——比如只打印奇数:
for i in range(1, 8):
if i % 2 == 0:
continue # 偶数直接跳过
print(i, end=" ") # 输入: range(1,8) → 输出: 1 3 5 7
注意看:continue 不是「从头开始」,而是「跳过本轮 continue 之后剩下的代码,直接进入下一轮」。
⚠️ 常见错误
- 把
break和continue用反:想跳出整个循环却用了continue,结果一直跳过不退出;想跳过当前轮却用了break,提前把循环结束了。 continue之后的代码不会执行:如果打印写在continue之后,被跳过的元素永远不会打印(这符合预期,但初学容易困惑)。break只跳出最近的一层循环:嵌套循环里想跳出外层,需要外层自己判断(2.5 节会讲)。
💡 记忆口诀
「break 是急刹车(整辆车停),continue 是这一站踩油门(跳过这站继续跑)。」
2.5 嵌套循环
循环里再套循环,叫嵌套循环。想象一张课程表:外层循环是星期几,内层循环是第几节课——每天都要把 4 节课走完,才会到第二天。所以内层跑完一轮,外层才 +1。
for i in range(1, 4): # 外层: 星期几
for j in range(1, 4): # 内层: 第几节
print(i, j) # 共 3×3 = 9 次
# 输入: 上面循环 → 输出: 1 1 / 1 2 / 1 3 / 2 1 / 2 2 / 2 3 / 3 1 / 3 2 / 3 3
嵌套循环最经典的两种题型:打印三角形和九九乘法表 ⭐。核心技巧是:让内层循环的次数跟着外层走,就能画出渐变的形状。
# 直角三角形: 第 i 行打印 i 个 *
for i in range(1, 4):
for j in range(i): # 内层循环次数 = 外层当前的 i
print("*", end="") # end="" 让星号在同一行连续打印
print() # 打完一行再换行
# 输出:
# *
# **
# ***
# 九九乘法表(核心考点,背下来)
for i in range(1, 10):
for j in range(1, i + 1): # 第 i 行只打印到 i
print(f"{j}x{i}={i*j}", end=" ")
print() # 每行结束换行
# 输出前两行:
# 1x1=1
# 1x2=2 2x2=4
这里两个细节是考点 ⭐:
print(..., end=""):默认print每打一次就换行,用end参数可以改成「不打换行、接空格或别的字符」。f"{j}x{i}={i*j}":格式化字符串,把变量填进花括号里,f开头、内容用引号包住。
⚠️ 常见错误
- 内层循环范围写错:乘法表里
range(1, i+1)写成range(1, 10),每行都打印 9 项,三角形也画歪了。 - 忘记在每行结束后
print()换行,所有内容挤成一坨。 - 用
end=""打印三角形后忘记换行,第一行*和第二行**会连成***。 - 混淆外层变量和内层变量:内层条件该用
i的地方用了j,图形就画歪了。
💡 记忆口诀
「外层跑得慢,内层跑得快;内层一轮完,外层进一步。谁管行、谁管列,画图先分清。」
2.6 常见计数套路(考试爱考)
循环配条件,能解决一大类「统计」问题。下面三个套路必须背下来:
# 套路一: 统计个数(数偶数)
nums = [3, 8, 1, 6, 10]
count = 0
for x in nums:
if x % 2 == 0:
count += 1
print(count) # 输入: nums=[3,8,1,6,10] → 输出: 3(8、6、10 三个偶数)
# 套路二: 求和(1..100 累加,高斯公式验证 = 5050)
total = 0
for i in range(1, 101):
total += i
print(total) # 输入: 1..100 → 输出: 5050
# 套路三: 找最值(不用内置 max)
nums = [3, 1, 4, 1, 5]
mx = nums[0] # 先假设第一个最大
for x in nums[1:]: # 从第二个开始逐个比较
if x > mx:
mx = x
print(mx) # 输入: nums=[3,1,4,1,5] → 输出: 5
三个套路长得像,但目的不同:计数用 count,求和用 total,找最值用「先假设、再逐个挑战」。考试时先看清题目要的是「个数」「总和」还是「最大/最小值」,别拿错变量。
找最值的另一种写法(把初始化放进循环里)容易出错,推荐上面的「先取第一个」写法,它天然能处理除空列表外的所有情况。
⚠️ 常见错误
- 计数时
count忘记初始化成 0,直接用会报NameError。 - 找最值时初始化
mx = 0:如果列表全是负数,最大值永远找不到(0 比所有负数都大)。正确做法是mx = nums[0]。 - 累加
+=写成了=+,变成每次赋值而非累加。 - 循环内忘记缩进:
count += 1放到循环外,只加了一次,答案全错。
💡 记忆口诀
「计数加一 count += 1,累加 total += x,找最大先拿第一个再比。」
2.7 数字拆位与取模技巧(% 和 //)⭐
处理数字题时,「取出一位」「丢掉一位」是两个高频动作,核心是 % 和 // 这对搭档:
n = 123
print(n % 10) # 输出: 3 (% 10 取个位)
print(n // 10) # 输出: 12 (// 10 丢掉个位)
print(n // 100) # 输出: 1 (// 100 丢掉后两位)
# 输入: n=123 → 输出: 3 / 12 / 1
配合 while,就能把任意长度的数字一位一位剥开(各位数字之和、数位个数、数字反转,全靠这套动作):
# 求各位数字之和: 123 → 1+2+3 = 6
n = 123
total = 0
while n > 0:
total += n % 10 # 取走个位
n //= 10 # 去掉个位
print(total) # 输出: 6
另一个经典应用是辗转相除法求最大公约数(gcd)⭐。思路很巧妙:gcd(a, b) 等于 gcd(b, a % b),一直套用直到余数为 0,剩下那个数就是答案:
# 求 48 和 36 的最大公约数
a, b = 48, 36
while b != 0:
a, b = b, a % b
print(a) # 输出: 12
# 过程: 48,36 → 36,12 → 12,0 → 余数为 0 结束,答案是 12
a, b = b, a % b 这行是同时赋值:右边的 b 和 a % b 先算好,再一起赋给 a 和 b,不会出现「用旧 a 算 b」的错位。
考试里「数字题」十有八九要用 % 和 //,练熟这套拆位动作,很多题就是套模板。
⚠️ 常见错误
n % 10和n // 10方向记反:% 10拿个位,// 10去个位,别搞混。- 拆位循环写成
while n >= 0,而 n 会被减到 0,就永远不结束(死循环)。应该用while n > 0。 - 处理 0 或负数时的边界:0 没有个位可拆(拆出来全是 0);负数取模结果和直觉不同,先转成正数再处理。
💡 记忆口诀
「% 10 剪指甲(拿个位),// 10 砍一刀(去个位)。一个取、一个去,数字一位位剥光。」
2.8 综合实战:循环 + 条件组合
把前面所有招数拼起来,就是考试里最后几道「大题」的样子。这里练两个经典综合题,正好对应本讲练习里最后的综合题。
例一:求区间内所有质数之和(嵌套循环 + 判断 + 累加三合一)
def sum_primes(n):
"""返回 2 到 n 之间所有质数之和。"""
total = 0
for x in range(2, n + 1): # 外层: 检查每个数
is_prime = True
for i in range(2, x): # 内层: x 能被谁整除?
if x % i == 0:
is_prime = False
break # 找到一个因子就够了,提前退出
if is_prime:
total += x # 只有质数才累加
return total
print(sum_primes(10)) # 输入: n=10 → 输出: 17(2+3+5+7)
print(sum_primes(20)) # 输入: n=20 → 输出: 77
思路拆解:外层循环负责「过一遍 2..n 每个数」,内层循环负责「判断这个数是不是质数」,最后只把质数累加。每个数字都有自己的「任务分工」,这就是嵌套循环的意义——一层管一件事。
例二:考拉兹猜想(Collatz)步数(while + 分支 + 计数)
规则:给一个正整数 n,偶数就除以 2,奇数就乘 3 加 1,反复操作直到变成 1,问要几步。
def collatz_steps(n):
"""返回 n 通过偶数÷2、奇数×3+1 到达 1 所需的步数。"""
steps = 0
while n != 1: # 没到 1 就继续
if n % 2 == 0:
n //= 2 # 偶数分支
else:
n = 3 * n + 1 # 奇数分支
steps += 1 # 每操作一次记一步
return steps
print(collatz_steps(6)) # 输入: n=6 → 输出: 8(6→3→10→5→16→8→4→2→1)
print(collatz_steps(1)) # 输入: n=1 → 输出: 0(本来就是 1,一步都不走)
这类综合题的通用解题顺序:① 读题确定「每轮做什么」→ ② 选 for 还是 while(次数已知用 for,条件未知用 while)→ ③ 决定循环里用不用 break/continue → ④ 想好要累计/统计哪个变量。按这个顺序写,不慌。
⚠️ 常见错误
- 把 while 写成 for:考拉兹循环次数不确定,得用
while;用for就得先算出步数,那就本末倒置了。 - 质数判断内层范围写错:
range(2, x)写成range(2, x+1),x % x == 0恒成立,所有数都被判成「不是质数」。 - 综合题变量名重复使用:外层变量
x和内层变量i混用,逻辑就乱了。 - 忘记给累计变量初始化(
total = 0/steps = 0),直接NameError。
💡 记忆口诀
「先想每轮干啥,再选 for 还是 while;break 提前收工,continue 跳过这轮;计数求和找最值,变量先归零再累加。」
📌 双语术语表(本讲)
| 中文 | English |
|---|---|
| 条件语句 | conditional / if statement |
| 循环 | loop |
| 迭代 | iteration |
| 迭代器 | iterator |
| 死循环 / 无限循环 | infinite loop |
| 跳出 | break |
| 跳过 | continue |
| 嵌套 | nested |
| 索引 | index |
| 步长 | step |
| 取余 / 模 | modulo / remainder |
| 整除 | floor division |
| 最大公约数 | greatest common divisor (GCD) |
| 质数 | prime number |
⭐ 本讲考点清单
range(start, stop, step)左闭右开==vs=、冒号、缩进break(跳出)vscontinue(跳过)while必须能终止,防死循环enumerate()同时拿索引和值- 嵌套循环(乘法表、三角形)
in判断元素是否存在- 计数/求和/找最值三大套路
%取个位、//去个位,拆位套路与辗转相除法- 综合题:质数之和、考拉兹步数(for/while + 条件组合)