5. 让程序自己做决定、重复劳动——控制流
读完本节,你能在 25 分钟内,用 if 让程序做判断、用 for / while 让程序重复劳动,并主动避开缩进、= vs ==、死循环 3 个最高频的坑。
程序之所以“活”,靠的是三种基本结构:顺序、选择、循环。本节把它们一次讲透。
5.1 三大结构概述
!三大结构流程图:顺序 / 选择 / 循环
三大控制流结构(无图,文字描述):
- 顺序
- 选择
- 循环:
for / while —— 满足条件就一直执行,直到条件不再满足
- 顺序
- 选择
- 循环:
for / while —— 满足条件就一直执行
5.2 if / elif / else
score = 85
if score >= 90:
print("优秀")
elif score >= 80: # elif = else if
print("良好")
elif score >= 60:
print("及格")
else:
print("不及格")
输出:
良好
⚠️ 注意:缩进在 Python 里是语法的一部分,不是美观。if 下面的代码块必须缩进 4 个空格(或 1 个 Tab,但不要混用)。
条件可以用 and / or 组合:
age = 25
has_ticket = True
if age >= 18and has_ticket:
print("可以入场")
elif age < 18:
print("未成年")
else:
print("没票")
嵌套 if 也允许(但层数多了会难读,能用 and 合并就合并):
if age >= 18:
if has_ticket:
print("可以入场")
else:
print("去补票")
else:
print("未成年,不能入场")
三元表达式一行搞定:
a, b = 5, 8
print(a if a > b else b) # 8(取较大值)
Python 3.10+ 还引入了 match-case(类似 switch,但更强大):
command = "save"
match command:
case"save":
print("保存")
case"load":
print("加载")
case _:
print("未知命令")
初学阶段用 if/elif/else 就够,后面遇到“模式匹配”再学 match。
5.3 for 循环:遍历已知集合
# 遍历列表
fruits = ["apple", "banana", "cherry"]
for f in fruits:
print(f)
# 遍历字符串
for ch in"hello":
print(ch)
# 用 range 生成数字序列
for i inrange(1, 6): # 1 到 5(含左不含右)
print(i)
range 的三种姿势:
range(5) # 0,1,2,3,4(从 0 开始)
range(1, 6) # 1,2,3,4,5(含左不含右)
range(0, 10, 2) # 0,2,4,6,8(步长 2)
range(10, 0, -1) # 10,9,8,...,1(倒数)
enumerate 同时拿索引和值:
fruits = ["apple", "banana", "cherry"]
for i, f inenumerate(fruits):
print(f"{i}: {f}")
enumerate 还支持指定起始值:
for i, f inenumerate(fruits, start=1): # 从 1 开始
print(f"{i}: {f}")
# 1: apple / 2: banana / 3: cherry
zip 把多个列表并行遍历:
names = ["小明", "小红", "小刚"]
scores = [85, 92, 78]
for name, score inzip(names, scores):
print(f"{name}: {score}")
长度不一致时,zip 以最短的为准:
a = [1, 2, 3]
b = ["a", "b"]
for x, y inzip(a, b):
print(x, y)
# 1 a / 2 b(第三个不遍历)
要遍历到最长,用 itertools.zip_longest(标准库)。
嵌套循环(循环里套循环)很常见,比如打印九九乘法表:
for i inrange(1, 10):
for j inrange(1, i + 1):
print(f"{j}×{i}={i*j}", end="\t")
print() # 每行结束换行
初学阶段,看到嵌套循环如果脑子乱了,就拿张纸画“i 走一遍,j 走几遍” —— 99% 的嵌套循环本质都是这个套路。
5.4 while 循环:条件为真就一直跑
count = 0
while count < 5:
print(count)
count += 1# 必须让条件有变 False 的一天
while 适合“不知道要循环多少次,只知道什么时候停”的场景(比如猜数字、监听用户输入)。
三个关键字:
# 找到第一个能被 7 整除的数就停
for n inrange(1, 100):
if n % 7 == 0:
print(f"找到了:{n}")
break
# 跳过偶数,只打印奇数
for n inrange(1, 11):
if n % 2 == 0:
continue
print(n)
for / while 后还可以接 else(Python 独特设计):循环正常结束(没被 break)时执行:
for n inrange(2, 5):
for x inrange(2, n):
if n % x == 0:
break
else: # 内层循环没被 break,说明 n 是质数
print(f"{n} 是质数")
# 输出:2 是质数 / 3 是质数
初学可以跳过 else 分支,进阶再回来看。
5.5 实战案例:用控制流判断闰年
闰年规则:能被 4 整除且不能被 100 整除,或者能被 400 整除。
defis_leap(year):
if year % 400 == 0:
returnTrue
if year % 100 == 0:
returnFalse
if year % 4 == 0:
returnTrue
returnFalse
print(is_leap(2000)) # True(400 的倍数)
print(is_leap(1900)) # False(100 的倍数但不是 400)
print(is_leap(2024)) # True(4 的倍数)
print(is_leap(2023)) # False
一行版(利用短路):
defis_leap(year):
return year % 400 == 0or (year % 4 == 0and year % 100 != 0)
打印指定年份范围的闰年:
defprint_leaps(start, end):
"""打印 start 到 end 之间所有闰年"""
count = 0
for year inrange(start, end + 1):
if is_leap(year):
print(year, end=" ")
count += 1
print(f"\n{start}-{end} 之间共 {count} 个闰年")
print_leaps(2000, 2025)
# 2000 2004 2008 2012 2016 2020 2024
# 2000-2025 之间共 7 个闰年
💡 小贴士:or 的优先级低于 and,所以括号可以省略,但写出来更清楚。用条件判断的代码,先写“对的”,再写“正常路径”。
坑 1:Tab 和空格混用
ifTrue:
print("hello") # 4 个空格
print("world") # 1 个 Tab
运行会报 TabError: inconsistent use of tabs and spaces。
!TabError 报错截图:TabError: inconsistent use of tabs and spaces
报错截图说明:终端输出 File "your_script.py", line 5\n if x > 0:\n ^\nTabError: inconsistent use of tabs and spaces,箭头指向混用了 Tab 与 4 空格的行。
🐛 避坑:在 VS Code 右下角点 "Spaces: 4",确保整个项目用同一种缩进。Python 官方推荐 4 空格。如果你从网上复制代码到自己的项目,要特别检查缩进 —— 很多网页会偷偷把空格变 Tab。
坑 2:=(赋值)和 ==(比较)混用
x = 5
if x = 5: # SyntaxError: invalid syntax
print("等于5")
📌 对比:
=== 是比较(问“左边和右边相等吗”,返回 True / False)
写条件判断时永远用 ==。
新手常见的另一种错误:在 if 里忘了写比较,Python 会默默不报错地做了赋值:
x = 5
if (x = 10): # SyntaxError(直接报错,Python 3.8+)
print(x)
# 但早期版本会警告,某些语言(C/JavaScript)会运行通过——务必写清楚
坑 3:死循环
whileTrue:
print("停不下来")
这个程序会一直跑,直到内存爆掉或者你按 Ctrl+C。
🐛 避坑:任何 while 必须确保循环体内有让条件变 False 的代码,或者明确写 break 的退出条件。
5.6 边遍历边删除:进阶坑
nums = [1, 2, 3, 4, 5]
for n in nums:
if n % 2 == 0:
nums.remove(n) # 危险!
print(nums) # [1, 3, 5]?不,可能是 [1, 3, 5] 或 [1, 3, 4, 5]
原因:for 循环依赖列表的索引,边删边遍历会让索引错位。
正确做法:遍历副本 / 列表推导式
nums = [1, 2, 3, 4, 5]
nums = [n for n in nums if n % 2 != 0] # 重建一个列表
print(nums) # [1, 3, 5] ✓
🎯 动手练:猜数字游戏
import random
target = random.randint(1, 100)
guess = 0
tries = 0
print("我想了一个 1 到 100 的整数,你猜是多少?")
while guess != target:
guess = int(input("请输入你的猜测:"))
tries += 1
if guess > target:
print("太大了")
elif guess < target:
print("太小了")
else:
print(f"猜对了!你用了 {tries} 次")
print("游戏结束")
进阶版(可选):限制最大猜测次数
import random
target = random.randint(1, 100)
tries = 0
max_tries = 7
print(f"你有 {max_tries} 次机会猜 1-100 的整数")
while tries < max_tries:
guess = int(input(f"第 {tries+1} 次猜:"))
tries += 1
if guess > target:
print("太大了")
elif guess < target:
print("太小了")
else:
print(f"猜对了!你用了 {tries} 次")
break
else: # 循环正常结束(没用 break)才执行
print(f"次数用完了!答案是 {target}")
第 6 节我们会把这个游戏封装成函数,第 7 节会进一步用 def guess_number(...) -> int 收束。
练习参考答案
完整代码见 S5 正文,关键是用 while guess != target + if / elif / else 提示。上面两段(基础版 + 进阶版可选)就是参考答案,自己写一遍后再回来对照思路。
本节小结
if / elif / elsefor 遍历集合,enumerate 拿索引,zip 并行多列表while 条件循环,break 跳出,continue 跳过,pass 占位- 三大坑:Tab/空格混用 → TabError;
= vs == 混淆 → SyntaxError;死循环 → 必须有终止条件