6.控制语句
控制语句
1 选择结构
前面学习中,我们写的都是很短的示例代码,没有涉及复杂操作。现在开始学习流程控制语句。
前面学的变量、数据类型(整数、浮点数、布尔)与序列(字符串、列表、元组、字典、集合)可以看作数据的组织方式;数据是「砖块」,流程控制语句则是代码的组织方式,是「混凝土」。一个完整的程序,两者缺一不可。
选择结构通过判断条件是否成立来决定执行哪个分支。它有多种形式,分为:单分支、双分支、多分支。流程图如下:


1.1 单分支选择结构
if 单分支结构的语法如下:
if 条件表达式:
语句/语句块
其中:
- 条件表达式:可以是逻辑表达式、关系表达式、算术表达式等。
- 语句/语句块:可以是一条语句,也可以是多条。多条语句时缩进必须对齐一致。
案例:输入一个数字,小于 10,则打印这个数字。
num = 5 # 原案例由 input() 接收输入,此处改为固定值便于复现
if num < 10:
print(num)
# 输出:5


1.2 条件表达式详解
因此,Python 所有的合法表达式都可以看做条件表达式,甚至包括函数调用的表达式。
# 整数作为条件表达式:非 0 即真
if 3:
print("ok")
# 输出:ok
# 空列表作为条件表达式是 False,not a 即 True
a = []
if not a:
print("空列表,False")
# 输出:空列表,False
# 非空字符串是 True——即便内容恰好是 "False" 这个词
s = "False"
if s:
print("非空字符串,是 True")
# 输出:非空字符串,是 True
c = 9
if 3 < c and c < 20:
print("3<c and c<20")
# 输出:3<c and c<20
# 大小于号可以连续使用(链式比较)
if 3 < c < 20:
print("3<c<20")
# 输出:3<c<20
逐项验证空值/零值的布尔值:
print(bool(False), bool(0), bool(0.0), bool(None))
# 输出:False False False False
print(bool(''), bool([]), bool(()), bool({}), bool(set()), bool(range(0)))
# 输出:False False False False False False
print(bool('False'), bool([0]), bool(-1))
# 输出:True True True —— 非空即真,与内容无关

条件表达式中,不能有赋值操作符 =
Python 中,条件表达式不能出现赋值操作符 =,这避免了其他语言里常把关系运算符 == 误写成赋值运算符 = 的困扰。如下代码会报语法错误:
if 3 < c and (c=20):
print("赋值符不能出现在条件表达式中")
# 实测:SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?

1.3 双分支选择结构
双分支结构的语法格式如下:
if 条件表达式:
语句 1/语句块 1
else:
语句 2/语句块 2
案例:输入一个数字,小于 10,则打印该数字;否则打印「数字太大」。
num = 15 # 原案例由 input() 接收输入,此处改为固定值便于复现
if int(num) < 10:
print(num)
else:
print("数字太大")
# 输出:数字太大


三元条件运算符
Python 提供了三元运算符,用来简化某些简单的双分支赋值场景。
三元条件运算符语法格式如下:
条件为真时的值 if (条件表达式) else 条件为假时的值
上面的案例用三元条件运算符实现:
num = 15
print(num if int(num) < 10 else "数字太大")
# 输出:数字太大


1.4 多分支选择结构
多分支选择结构的语法格式如下:
if 条件表达式 1:
语句 1/语句块 1
elif 条件表达式 2:
语句 2/语句块 2
...
elif 条件表达式 n:
语句 n/语句块 n
[else:
语句 n+1/语句块 n+1]

多分支结构,几个分支之间是有逻辑关系的,不能随意颠倒顺序。
案例:输入一个学生成绩,将其转换为简单描述:不及格(小于 60)、及格(60-79)、良好(80-89)、优秀(90-100)。
方法 1:使用多个独立的 if 完整条件表达。
score = 85 # 原案例由 input() 接收输入,此处改为固定值便于复现
grade = ''
if score < 60:
grade = "不及格"
if 60 <= score < 80:
grade = "及格"
if 80 <= score < 90:
grade = "良好"
if 90 <= score <= 100:
grade = "优秀"
print(f"分数是{score},等级是{grade}")
# 输出:分数是85,等级是良好
每个分支都使用了独立的、完整的判断,顺序可以随意挪动,而不影响程序运行。

方法 2:利用多分支结构(elif 链)。
score = 85
grade = ''
if score < 60:
grade = "不及格"
elif score < 80:
grade = "及格"
elif score < 90:
grade = "良好"
elif score <= 100:
grade = "优秀"
print(f"分数是{score},等级是{grade}")
# 输出:分数是85,等级是良好

案例:已知点的坐标 (x, y),判断其所在的象限。
x, y = 2, 5 # 原案例由 input() 接收输入,此处改为固定值便于复现
if x == 0 and y == 0:
print("原点")
elif x == 0:
print("y 轴")
elif y == 0:
print("x 轴")
elif x > 0 and y > 0:
print("第一象限")
elif x < 0 < y:
print("第二象限")
elif x < 0 and y < 0:
print("第三象限")
else:
print("第四象限")
# 输出:第一象限

1.5 选择结构嵌套
选择结构可以嵌套使用。注意控制好不同级别代码块的缩进量,因为缩进量决定了代码的从属关系。
语法格式如下:
if 表达式 1:
语句块 1
if 表达式 2:
语句块 2
else:
语句块 3
else:
if 表达式 4:
语句块 4
案例:输入一个分数(0-100 之间),90 及以上是 A,80 及以上是 B,70 及以上是 C,60 及以上是 D,60 以下是 E。
score = 96 # 原案例由 input() 接收输入,此处改为固定值便于复现
grade = ""
if score > 100 or score < 0:
print("输入错误!请重新输入一个在 0-100 之间的数字")
else:
if score >= 90:
grade = "A"
elif score >= 80:
grade = 'B'
elif score >= 70:
grade = 'C'
elif score >= 60:
grade = 'D'
else:
grade = 'E'
print(f"分数为{score},等级为{grade}")
# 输出:分数为96,等级为A

也可以用下面这段代码更少的方法——先想想为什么这么写(答案见代码后的 [!tip])。
score = 96
degree = "ABCDE"
num = 0
if score > 100 or score < 0:
print("输入错误!请重新输入一个在 0-100 之间的数字")
else:
num = score // 10
if num < 6:
num = 5
print(f"分数是{score},等级是{degree[9 - num]}")
# 输出:分数是96,等级是A

2 循环结构
2.1 什么是循环
现实生活中,有很多循环的应用场景:
- 食堂阿姨打菜:接过顾客的餐盘 → 询问菜品 → 打菜 → 递回餐盘,重复以上过程,直到所有顾客的菜都打完了。
- 快递员送快递:查看送件地址 → 赶往目的地 → 电话告知收件人 → 收件人签收 → 交快递件,重复以上过程,直到所有需要送的快递都处理完了。
- 公交司机……
- 作业流程……
以上场景有一个共同特点:有条件地重复做一件事,每次做的事情不同但类似。
程序是为了解决实际问题而存在的。实际问题中存在着重复动作,那么程序中也应有相应的描述,这就是循环。
2.2 循环的作用
循环结构用来重复执行一条或多条语句。它表达这样的逻辑:符合条件时反复执行循环体里的语句;每次执行完后都会再判断一次条件是否为 True,为 True 则继续重复执行。图示如下:

循环体里至少应该包含改变条件表达式的语句,使循环趋于结束;否则就会变成死循环。
2.3 循环的种类
在 Python 中,循环分为两大类:while 循环与 for 循环。

2.4 while 循环
while 循环的语法格式如下:
while 条件表达式:
循环体语句
while 循环的基本语法
编写 while 循环的固定套路(语法模板,非可执行代码):
1. 定义一个计数器(初始化一个计数器):i = 0 或 1
2. 编写 while 循环结构:
while 循环条件(判断计数器是否达到了目标位置):
循环体 1
循环体 2
...
# 3. 在循环体内部更新计数器
i = i + 1 或 i += 1
while 循环入门案例:使用 while 循环输出 100 遍「老婆大人,我错了」。
# 1. 初始化计数器
i = 0
# 2. 编写循环条件(判断计数器是否达到了 100)
while i < 100:
print('老婆大人,我错了')
# 3. 在循环体内部更新计数器
i += 1

while 循环的执行流程
建议使用 Debug 调试工具查看 while 循环的运行流程。
# 1. 初始化计数器
i = 0
# 2. 编写循环条件(判断计数器是否达到了 100)
while i < 100:
print('老婆大人,我错了')
# 3. 在循环体内部更新计数器
i += 1
下断点,开始 Debug 调试:

- 代码顺序执行:先执行到第 2 行,为变量 i 赋值,初始值为 0。
- 遇到 while 循环时,先把计数器与循环条件比较
i < 100:成立则进入循环内部执行代码。 - 在循环体内部,针对本次循环更新计数器
i = i + 1(或i += 1)。 - 更新完毕后,把更新后的计数器与循环条件再次比较
i < 100:成立则继续进入循环执行。 - ……
- 直到 i 更新后为 100,再次判断循环条件发现不满足,整个 while 循环结束。
while 循环流程图:

while 循环案例
案例 1:使用 while 循环求 1~100 的和。
分析:定义一个 while 循环,让其帮助我们计算 1 + 2 + 3 + 4 + 5 … + 100,结果:5050。
大问题拆解为小问题:
- 使用 while 循环,循环 100 次。
# 第一步:初始化计数器
i = 1
# 第二步:编写循环条件
while i <= 100:
print(i) # 1 2 3 4 5 6 7 8 9 10...
# 第三步:更新计数器的值
i += 1
- 在循环体内部,累计求和——先定义一个变量,用于得到最终的运算结果:
# 第四步:定义一个变量 result = 0,用于接收累加后的结果
# 第五步:想办法让 result = 变量 i 累加后的结果
- 最终代码:
# 第一步:初始化计数器
i = 1
# 第四步:定义一个 result 变量,用于接收累加后的结果
result = 0
# 第二步:编写循环条件
while i <= 100:
# 第五步:循环累加变量 i
result += i
# 第三步:更新计数器的值
i += 1
print(f'1~100 累加后的结果:{result}')
# 输出:1~100 累加后的结果:5050

- 首先,定义变量 i 和变量 result,进行初始化赋值。
- 然后,判断变量 i 是否满足循环条件,如果满足循环条件,则进入到循环体内部,执行内部代码。
思考:如何让变量 i 累加后赋给 result?直接写 result = i 不行——每轮循环只是覆盖,没有累加。第一次循环时 i = 1、result = 0,要得到累加结果应写 result = result + i,即:
result = 0 + 1
计数器更新,i += 1 后 i 变成 2,i <= 100 成立,继续执行循环内部代码:
result = result + i # 代入数值即 result = 1 + 2
依次类推,每轮都是 result = result + i,简写为:
result += i
案例 2:求 1~100 之间,所有偶数的和。
什么是偶数:能被 2 整除的数就是偶数,数学中用 2n 表示(Python 中即 if 数值 % 2 == 0)。
知识点:在 while 循环中还可以结合 if 进行判断。
第一步:求出 1~100 之间所有的数。
# 初始化计数器
i = 1
# 编写循环条件
while i <= 100:
# 将来写代码的位置
# 更新计数器
i += 1
第二步:在循环体中引入 if 条件判断,判断变量 i 是否为一个偶数。
# 初始化计数器
i = 1
# 编写循环条件
while i <= 100:
# 将来写代码的位置
if i % 2 == 0:
# 代表变量 i 是一个偶数
print(i)
# 更新计数器
i += 1
第三步:引入 result 变量,初始值为 0,然后对第二步中得到的所有偶数进行累加。
# 初始化计数器
i = 1
# 定义 result,用于接收所有偶数的和
result = 0
# 编写循环条件
while i <= 100:
# 将来写代码的位置
if i % 2 == 0:
# 代表变量 i 是一个偶数
result += i
# 更新计数器
i += 1
print(f'1~100 之间所有偶数的和:{result}')
# 输出:1~100 之间所有偶数的和:2550

编写循环时常见问题
i = 1
result = 0
while i <= 100:
if i % 2 == 0:
result += i
i += 1
print(f'1~100 之间所有偶数的和:{result}')

问题的原因:i += 1 多缩进了一层,导致它和 result += i 一起从属于 if。当 i 是奇数时 if 条件不成立,i += 1 不执行——while 循环中计数器永远无法更新,最终导致死循环(实测该代码 4 秒内不会结束,验证脚本用子进程超时证实)。
修复:让 i += 1 与 if 对齐(回到 while 的直接从属层级),见案例 2 第三步的正确代码。
2.5 for 循环
for 循环基本语法
for 循环主要用于序列(字符串、列表、元组)、集合、字典等可迭代对象的遍历(循环)操作。
for 临时变量 in 序列:
重复执行的代码 1
重复执行的代码 2
案例:使用 for 循环遍历字符串 "小龙女"。
str1 = '小龙女'
for i in str1:
print(i)
# 输出:
# 小
# 龙
# 女

用 Debug 调试以上代码可知:for 循环会自动按序列长度决定循环次数——长度是多少就循环多少次;每次循环时系统自动把序列中的当前元素赋给变量 i,然后前进到下一个元素,直到元素全部遍历完。
可迭代对象
Python 包含以下几种可迭代对象:
- 序列:字符串、列表、元组
- 字典、集合
- 迭代器对象(iterator)
- 生成器函数(generator)
- 文件对象
前面已学过序列、字典等知识,迭代器对象和生成器函数将在后面详解。接下来用循环遍历这几种类型的数据。
案例 1:遍历字符串中的字符。
for x in "Hello":
print(x, end=' ')
# 输出:H e l l o

案例 2:遍历字典。
d = {'name': 'gaoqi', 'age': 18, 'address': '西三旗 001 号楼'}
# 遍历字典所有的 key
for x in d:
print(x)
print('#################')
# 遍历字典所有的 key(等价写法)
for x in d.keys():
print(x)
print('#################')
# 遍历字典所有的 value
for x in d.values():
print(x)
print('#################')
# 遍历字典所有的"键值对"
for x in d.items():
print(x)
# 输出(汇总):
# name / age / address
# #################
# name / age / address
# #################
# gaoqi / 18 / 西三旗 001 号楼
# #################
# ('name', 'gaoqi') / ('age', 18) / ('address', '西三旗 001 号楼')

range 对象
Python 2 的 range() 返回的是列表;而 Python 3 中 range() 返回的是一个可迭代对象(类型是对象),而不是列表类型,所以打印的时候不会打印出列表(由于我们还未学习面向对象,为了方便理解,你可以简单将其理解为一个序列结构)。
主要作用:用于生成一段连续的整数,如从 0 到 9。
基本语法:
range(stop)
range(start, stop[, step])
1. start:计数从 start 开始,默认从 0 开始。
例如 range(5) 等价于 range(0, 5)。
2. stop:计数到 stop 结束,但不包括 stop。
例如 range(0, 5) 是 [0, 1, 2, 3, 4],没有 5。
3. step:步长,默认为 1。
例如 range(0, 5) 等价于 range(0, 5, 1)。
print(list(range(5))) # [0, 1, 2, 3, 4] —— 不含 5
print(list(range(0, 5))) # 与 range(5) 相同,start 默认 0
print(list(range(0, 10, 2))) # [0, 2, 4, 6, 8] —— step 步长为 2
print(len(range(1, 101))) # 100 —— 1~100 共 100 个数
案例:for 循环与 range 配合,循环 5 次。
for i in range(5):
print(i, end=' ')
# 输出:0 1 2 3 4
for 循环案例
案例 1:使用 for 循环,求 1~100 的和。
# 定义一个变量,用于接收 1~100 的和
result = 0
# 从 1 开始循环,循环 100 次
for i in range(1, 101):
result += i
print(f'1~100 的和为{result}')
# 输出:1~100 的和为5050

案例 2:使用 for 循环,求 1~100 之间所有偶数的和。
# 定义一个变量,用于接收 1~100 之间所有偶数的和
result = 0
# 从 1 开始循环,循环 100 次
for i in range(1, 101):
if i % 2 == 0:
result += i
print(f'1~100 之间所有偶数的和为{result}')
# 输出:1~100 之间所有偶数的和为2550

2.6 嵌套循环
一个循环体内可以嵌入另一个循环,一般称为「嵌套循环」或「多重循环」。
嵌套循环的基本语法
先回顾一个简单的 while 循环结构:
# 初始化计数器
i = 0 或 i = 1
# 编写循环条件
while i < 边界值:
循环体代码
# 更新计数器
i += 1
所谓 while 嵌套循环,就是在 while 循环的基础上,把「循环体代码」换成一层 while 循环,从而组成 while 嵌套循环。
# 第一步:初始化外层循环计数器
i = 1
# 第二步:编写外层循环的条件
while i <= 3:
# 第四步:初始化内层循环计数器
j = 1
# 第五步:编写内层循环的条件
while j <= 3:
print(f'i={i}, j={j}') # 循环体代码
# 第六步:更新内层循环计数器
j += 1
# 第三步:更新外层循环计数器
i += 1
# 输出:i=1,j=1 … i=1,j=3 / i=2,j=1 … i=2,j=3 / i=3,j=1 … i=3,j=3
结构如下:

所谓 for 循环嵌套,就是一个 for 循环里再嵌套一个 for 循环。
循环结构相互嵌套时,外层的循环结构常简称为外层循环(外循环),内层常简称为内层循环(内循环)。
基本语法:
# 外层循环
for i in 序列 1:
# 内层循环
for j in 序列 2:
循环体
嵌套循环的执行流程
while 循环嵌套执行流程口诀:在 while 循环嵌套的执行过程中,外层循环 1 次,内层循环 N 次(类比时钟:分针是外层循环,秒针是内层循环——分针走 1 格,秒针走一整圈)。
想了解 while 嵌套循环的执行流程,建议用 Debug 调试。
循环嵌套的总次数 = 外层循环次数 × 内层循环次数(实测:外层 3 次 × 内层 4 次,循环体共执行 12 次)。
嵌套循环的几个案例
案例 1:打印 5 行 5 列的正方形,单元格中使用 * 号进行填充。

示例代码:
# 方法一:直接 print 打印
# print("* * * * *")
# print("* * * * *")
# print("* * * * *")
# print("* * * * *")
# print("* * * * *")
# 方法二:使用单层 while 循环
# i = 1
# while i <= 5:
# print("* * * * *")
# i += 1
# 方法三:使用单层 while 循环 + 运算符
# i = 1
# while i <= 5:
# print("* " * 5)
# i += 1
# 方法四:使用 while 循环嵌套
i = 1
while i <= 5:
j = 1
while j <= 5:
if j == 5:
print("*")
else:
print("* ", end='')
j += 1
i += 1
# 方法五:使用 for 循环嵌套
for i in range(5):
for j in range(5):
if j == 4:
print("*")
continue
print('*', end=' ')
# 输出(两个版本相同):5 行「* * * * *」
用 Debug 工具调试 while 嵌套循环可知:在正方形案例中,外层 while 控制行(row),内层 while 控制列(col)。
案例 2:打印直角三角形。特征:一共有 5 行,第 1 行有 1 列,第 2 行有 2 列,第 3 行有 3 列……

分析:如果采用 while 循环嵌套,外层应该循环 5 次,内层循环次数有一定的规则:
- 第 1 行内循环执行 1 次。
- 第 2 行内循环执行 2 次。
- 第 3 行内循环执行 3 次。
- 第 4 行内循环执行 4 次。
- 第 5 行内循环执行 5 次。
# 定义外层循环计数器
i = 1
# 编写外层循环的循环条件
while i <= 5:
# 定义内层循环计数器
j = 1
# 编写内层循环的循环条件
while j <= i:
if j == i:
print("*")
else:
print("*", end=' ')
j += 1
# 更新外层循环计数器
i += 1
# for 循环实现
for i in range(1, 6):
for j in range(i):
if j == i - 1:
print("*")
else:
print("*", end=' ')
# 输出:5 行直角三角形,第 i 行 i 个 *
案例 3:使用 while 嵌套循环打印 9 × 9 乘法表。

第一步:使用 while 嵌套循环打印 9 行的直角三角形。
# 初始化外层循环计数器
i = 1
# 编写外层的循环条件
while i <= 9:
# 初始化内层循环计数器
j = 1
# 编写内层的循环条件
while j <= i:
print('*', end=' ')
# 更新内层循环的计数器
j += 1
print('')
# 更新外层循环计数器
i += 1
第二步:把小星星换成具体的数值。
# 初始化外层循环计数器
i = 1
# 编写外层的循环条件
while i <= 9:
# 初始化内层循环计数器
j = 1
# 编写内层的循环条件
while j <= i:
if j == i:
print(f'{j} x {i} = {i * j}')
else:
print(f'{j} x {i} = {i * j}', end='\t')
# 更新内层循环的计数器
j += 1
# 更新外层循环计数器
i += 1
for 循环实现:
for row in range(1, 10):
for col in range(1, row + 1):
if col == row:
print(f'{col} x {row} = {row * col}')
else:
print(f'{col} x {row} = {row * col}', end='\t')
总结:外层循环主要用于控制循环的行数,内层循环用于控制列数。
2.7 break 语句
break 语句可用于 while 和 for 循环,用来结束整个循环。当有嵌套循环时,break 语句只能跳出最近一层的循环(实测:内层 j == 2 时 break,外层 3 轮各自只跑 2 次内层,共 6 次——外层不受影响)。
案例:使用 break 语句结束循环。
while True:
a = input("请输入一个字符(输入 Q 或 q 结束)")
if a.upper() == 'Q':
print("循环结束,退出")
break
else:
print(a)
2.8 continue 语句
continue 语句用于结束本次循环,继续下一次。多个循环嵌套时,continue 也是作用于最近的一层循环。
案例:要求输入员工的薪资,若薪资小于 0 则重新输入。最后打印出录入员工的数量和薪资明细,以及平均薪资。
emp_num = 0
salary_sum = 0
salaries = []
while True:
s = input("请输入员工的薪资(按 Q 或 q 结束):")
if s.upper() == 'Q':
print("录入完成,退出!!!")
break
if float(s) < 0:
print("员工薪资不得小于 0,请重新输入!!!")
continue
emp_num += 1
salaries.append(float(s))
salary_sum += float(s)
print(f"员工数{emp_num}")
print("录入薪资:", salaries)
print(f"平均薪资{salary_sum / emp_num}")

break 与 continue 的区别一图流(实测验证):
for i in range(5):
if i == 2:
continue # 跳过 2,继续后面的循环
if i == 4:
break # 到 4 直接终止,5 不会输出
print(i, end=' ')
# 输出:0 1 3
2.9 while 循环与 for 循环中的 else 结构
为什么需要在循环中添加 else 结构
循环可以和 else 配合使用,else 下方缩进的代码指的是当循环正常结束之后要执行的代码。
强调:只有循环正常结束时才会执行;循环非正常结束(如遇到 break 的情况),else 中的代码是不会执行的。
while 循环中 else 的基本语法
语法格式:
while 条件表达式:
循环体
else:
语句块
或者:
for 变量 in 可迭代对象:
循环体
else:
语句块
需求:女朋友生气了,要惩罚:连续说 5 遍"老婆大人,我错了",如果道歉正常完毕后女朋友就原谅我了,这个程序怎么写?
先看不带 else 的版本:
# 初始化计数器
i = 0
# 编写循环条件
while i < 5:
print('老婆大人,我错了')
# 更新计数器
i += 1
# 循环结束后,女朋友就原谅我了
print('好开森,女朋友原谅我了...')
思考:这个 print 是不是没有循环也能执行?那该如何解决?
答:使用 while…else 结构,把「原谅」和「道歉正常完毕」绑定成一个整体:
# 初始化计数器
i = 0
# 编写循环条件
while i < 5:
print('老婆大人,我错了')
# 更新计数器
i += 1
else:
# 只有循环正常结束(没有被 break 打断)才会执行
print('好开森,女朋友原谅我了...')
# 输出:5 遍道歉 + 好开森,女朋友原谅我了...

break 关键字对 while…else 结构的影响
需求:女朋友生气,要求道歉 5 遍「老婆大人,我错了」。道歉到第三遍时,她埋怨这一遍说得不真诚,是不是就要退出循环?退出有两种可能:
- 更生气,不打算原谅,也不需要道歉了,程序如何书写?
- 只一遍不真诚,可以忍受,继续下一遍道歉,程序如何书写?
第 1 种用 break 实现:
# 初始化计数器
i = 0
# 编写循环条件
while i < 5:
if i == 2:
print('这遍说的不够真诚')
break
print('老婆大人,我错了')
# 更新计数器
i += 1
else:
print('好开森,女朋友原谅我了...')
# 输出:2 遍道歉 + 这遍说的不够真诚(else 分支没有输出)

由运行结果可知,如果我们在 while 循环中使用了 break,一旦 break 语句执行,else 语句就不会输出。
continue 关键字对 while…else 结构的影响
# 初始化计数器
i = 0
# 编写循环条件
while i < 5:
if i == 2:
i += 1
print('这遍说的不够真诚')
continue
print('老婆大人,我错了')
# 更新计数器
i += 1
else:
print('好开森,女朋友原谅我了...')
# 输出:5 遍道歉(跳过第 3 遍,额外一句埋怨)+ 好开森,女朋友原谅我了...

由运行结果可知,如果我们在 while 循环中使用了 continue,continue 语句执行后,else 语句依然会输出(循环最终是正常结束的)。
for 循环结构中的 else 结构
基本语法:
for 临时变量 in 序列:
循环体
else:
当 for 循环正常结束后,执行的代码
for…else 最经典的实战用法是「搜索后没找到」:
for ch in "Hello":
if ch == 'x':
print('找到 x')
break
else:
print('没找到 x')
# 输出:没找到 x
break 关键字对 for…else 结构的影响
str1 = 'Hello'
for i in str1:
if i == 'e':
print('遇 e 不打印')
break
print(i)
else:
print('循环正常结束之后执行的代码')
# 输出:H / 遇 e 不打印(else 分支没有输出)

continue 关键字对 for…else 结构的影响
str1 = 'Hello'
for i in str1:
if i == 'e':
print('遇 e 不打印')
continue
print(i)
else:
print('循环正常结束之后执行的代码')
# 输出:H / 遇 e 不打印 / l / l / o / 循环正常结束之后执行的代码

2.10 循环代码优化
虽然计算机越来越快、空间也越来越大,我们仍要在性能上「斤斤计较」。编写循环时遵守下面三个原则,可以大大提高运行效率、避免不必要的低效计算:
- 尽量减少循环内部不必要的计算
- 嵌套循环中,尽量减少内层循环的计算,尽可能向外提。
- 局部变量查询较快,尽量使用局部变量
# 循环代码优化测试
import time
start = time.time()
for i in range(1000):
result = []
for m in range(10000):
result.append(i * 1000 + m * 100)
end = time.time()
print("未优化耗时:{0}".format((end - start)))
# 优化后:把 i * 1000 提到内层循环之外
start2 = time.time()
for i in range(1000):
result = []
c = i * 1000
for m in range(10000):
result.append(c + m * 100)
end2 = time.time()
print("优化后耗时:{0}".format((end2 - start2)))
# 本机实测输出(Python 3.13.14,不同机器数值会不同):
# 未优化耗时:1.260…
# 优化后耗时:1.079…

其他优化手段
- 连接多个字符串,使用
join()而不使用+。实测(50000 次拼接):+循环约 32ms,join()约 0.2ms;规模越大差距越大(+=约 O(n²)、join为 O(n),见 5.内建数据结构)。 - 列表进行元素插入和删除,尽量在列表尾部操作。尾部
append()/pop()是 O(1);头部insert(0, x)/pop(0)需要整体挪动元素,是 O(n)。
2.11 使用 zip() 并行迭代
可以用 zip() 函数对多个序列进行并行迭代;zip() 在最短序列「用完」时就会停止。
names = ("小龙女", "赵敏", "周芷若", "黄蓉")
ages = (18, 16, 20, 25)
jobs = ("古墓派掌门", "郡主", "峨眉派掌门") # 只有 3 个元素,最短
for name, age, job in zip(names, ages, jobs):
print("{0}--{1}--{2}".format(name, age, job))
# 输出:
# 小龙女--18--古墓派掌门
# 赵敏--16--郡主
# 周芷若--20--峨眉派掌门

实测:三个序列长度为 4、4、3,zip 只产出 3 行——「黄蓉」因为没有对应的 job 被丢弃。
3 推导式生成序列
3.1 什么是推导式
推导式 comprehensions(又称解析式)是 Python 的一种独有特性。它可以从一个数据序列构建另一个新的数据序列。共有三种:列表推导式、集合推导式、字典推导式(另有生成器表达式,见 §3.8,但它产出的是生成器而非容器)。
3.2 为什么需要推导式
案例:创建一个 0-9 的列表。
for 循环版:
list1 = []
# 编写 for 循环
for i in range(0, 10):
list1.append(i)
print(list1)
# 输出:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


3.3 列表推导式
基本语法:
变量名 = [表达式 for 变量 in 列表]
变量名 = [表达式 for 变量 in 列表 if 条件]
案例:定义 0-9 之间的列表。
先看普通写法:
list1 = []
for i in range(10):
list1.append(i)
print(list1)
# 输出:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
列表推导式:
list1 = [i for i in range(10)]
print(list1)
# 输出:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

执行原理(以 [i for i in range(10)] 为例):
列表推导式先运行表达式右侧的内容:
当第一次遍历时:i = 0,把变量 i 的结果放入最左侧的表达式中,这个时候列表中就是 [0]
当第二次遍历时:i = 1,其结果追加到列表中,这个时候列表中就是 [0, 1]
...
当最后一次遍历时:i = 9,这个时候列表中就是 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3.4 列表推导式 + if 条件判断
在列表推导式的遍历过程中,还可以引入 if 条件判断。
变量 = [表达式 for 临时变量 in 序列 if 条件判断]
等价于
for 临时变量 in 序列:
if 条件判断:
表达式加入结果
案例:生成 0-9 之间的偶数(i % 2 == 0)序列。
list1 = [i for i in range(10) if i % 2 == 0]
print(list1)
# 输出:[0, 2, 4, 6, 8]

3.5 for 循环嵌套列表推导式
这是对应两层 for 循环的推导式写法。原生嵌套循环的骨架如下(语法示意,非可执行代码):
for 临时变量 in range(n):
for 临时变量 in range(n):
表达式
基本语法:
变量 = [表达式 for 临时变量 in 序列 1 for 临时变量 in 序列 2]
案例:创建列表 => [(1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]。
原生代码:for 循环嵌套。
list1 = []
# 外层循环
for i in range(1, 3):
# 内层循环
for j in range(0, 3):
tuple1 = (i, j)
list1.append(tuple1)
print(list1)
# 输出:[(1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

列表推导式:
list1 = [(x, y) for x in range(1, 3) for y in range(3)]
print(list1)
# 输出:[(1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

3.6 字典推导式
思考:有如下两个列表,如何快速合并为一个字典?
list1 = ['name', 'age', 'gender']
list2 = ['Tom', 20, 'male']
# 目标:person = {'name':'Tom', 'age':20, 'gender':'male'}
答:使用字典推导式。它是列表推导式思想的延续,语法相近,只是产出的是字典。
基本格式:
变量 = {key: value for key, value in 序列}
字典推导式的作用:快速把列表合并为字典,或提取字典中的目标数据。
案例 1:创建一个字典:字典 key 是 1-5 数字,value 是这个数字的 2 次方。目标:dict1 = {1:1, 2:4, 3:9, 4:16, 5:25}。
dict1 = {i: i ** 2 for i in range(1, 6)}
print(dict1)
# 输出:{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

案例 2:把两个列表合并为一个字典。
写法一:按下标取。
list1 = ['name', 'age', 'gender']
list2 = ['Tom', 20, 'male']
person = {list1[i]: list2[i] for i in range(len(list1))}
print(person)
# 输出:{'name': 'Tom', 'age': 20, 'gender': 'male'}

写法二:使用 zip() 函数(更 Pythonic,两列表长度不齐时自动按最短截断)。
list1 = ['name', 'age', 'gender']
list2 = ['Tom', 20, 'male']
person = {key: value for key, value in zip(list1, list2)}
print(person)
# 输出:{'name': 'Tom', 'age': 20, 'gender': 'male'}

案例 3:提取字典中目标数据。
counts = {'MBP': 268, 'HP': 125, 'DELL': 201, 'Lenovo': 199, 'ACER': 99}
# 需求:提取上述电脑数量大于等于 200 的字典数据
counts = {key: value for key, value in counts.items() if value >= 200}
print(counts)
# 输出:{'MBP': 268, 'DELL': 201}

3.7 集合推导式
集合推导式与列表推导式非常相似,唯一区别是用 {} 代替 []。
需求:创建一个集合,数据为下方列表的 2 次方。
list1 = [1, 1, 2]
# 目标:set1 = {1, 4}(1 的平方 1 出现两次,自动去重)
推导式:
list1 = [1, 1, 2]
set1 = {i ** 2 for i in list1}
print(set1)
# 输出:{1, 4}

3.8 生成器表达式(不能生成元组)
很多人会问:都有了推导式,元组有没有?能不能用小括号?
res = (x for x in range(1, 100) if x % 9 == 0)
print(res)
# 输出:<generator object <genexpr> at 0x…>

可以看到提示的是一个「生成器对象」。显然,元组没有推导式——小括号写法得到的是生成器表达式。
生成器只能运行一次:第一次迭代能取到数据,第二次迭代时数据已被取光:
gnt = (x for x in range(1, 100) if x % 9 == 0)
for x in gnt:
print(x, end=' ')
print()
print('##############')
for x in gnt:
print(x, end=' ')
# 输出:
# 9 18 27 36 45 54 63 72 81 90 99
# ##############
# (第二次什么都不输出)

实测:1~99 中 9 的倍数共 11 个(9, 18, 27, 36, 45, 54, 63, 72, 81, 90, 99——原文截图口径为 range(1, 100),99 也命中)。
3.9 生成器表达式和列表推导式对比
| 对比项 | 生成器表达式 | 列表推导式 |
|---|---|---|
| 计算方式 | 延迟计算(用到才产出) | 立即计算 |
| 返回对象 | 可迭代对象(迭代器),可以迭代 | 可迭代对象(列表),不是迭代器 |
| 迭代次数 | 只能迭代一次 | 可反复迭代 |
| 内存占用 | 极少(不存数据) | 构造并持有整个新列表 |
- 计算方式
- 生成器表达式延迟计算:只有迭代到某个元素时才计算它。
- 列表解析式立即计算:创建时就求值所有元素。
- 内存占用
- 单从返回值本身来说,生成器表达式省内存,列表解析式返回并持有新的列表。
- 生成器本身不含数据,内存占用极少(实测:10 万元素生成器只创建不迭代仅分配 392B,同规模列表约 440KB);使用时逐个返回数据,不会立即占用大量内存。
- 列表解析式构造新列表需要立即占用内存。
- 计算速度
- 单看「拿到返回值」的时间,生成器表达式耗时非常短(因为它根本没算),列表解析式耗时长。
- 但生成器并没有返回任何数据,只返回了一个生成器对象——真正消费数据时的总计算量两者相同。
- 列表解析式构造并返回了一个新的列表。
3.10 补(了解)
-
序列:
常见的序列有list、tuple、str、bytes、bytearray。字节串
bytes、字节数组bytearray是二进制数据组成的序列,其中每个元素由 8bit 二进制组成(遍历时每个元素是一个 0~255 的 int,实测b'ab'遍历得到[97, 98])。 -
字节数组
bytearray:
可变的字节序列,相当于bytes的可变版本(实测:ba = bytearray(b'ab')后ba[0] = 65,得到bytearray(b'Ab'))。
4 术语表(重要英文单词)
4.1 结构与语句
| 术语 | 全拼 / 说明 | 释义 |
|---|---|---|
| if / elif / else | elif = else if | 选择结构关键字;elif 可叠加多个,else 至多一个 |
| while | — | 条件循环,条件为 True 时反复执行循环体 |
| for … in | — | 遍历循环,逐个取出可迭代对象的元素 |
| break | — | 立即终止最近一层整个循环 |
| continue | — | 跳过本次循环剩余语句,进入下一次循环 |
| loop | — | 循环 |
| nested loop | — | 嵌套循环 / 多重循环 |
| dead loop / infinite loop | — | 死循环;条件永不失效导致循环无法结束 |
| iteration | 迭代 | 依次取出集合中元素的过程 |
| iterable | — | 可迭代对象;实现了 __iter__、能被 for 遍历的对象 |
| iterator | — | 迭代器;有 __next__ 的对象,一次性消费 |
| truthy / falsy | — | 真值 / 假值;条件表达式按此判定真假(0、空容器等为 falsy) |
| chained comparison | — | 链式比较;a < b < c 等价 a < b and b < c |
| walrus operator | 海象运算符 := |
3.8+;在表达式内部赋值(PEP 572) |
| conditional expression | 条件表达式 | 三元运算符:A if 条件 else B |
4.2 推导式与工具
| 术语 | 全拼 / 说明 | 释义 |
|---|---|---|
| comprehension | 推导式 / 解析式 | 从一个序列构建另一个序列的紧凑语法 |
| list comprehension | 列表推导式 | [表达式 for x in 序列 if 条件],立即构建列表 |
| dict comprehension | 字典推导式 | {k: v for ...},构建字典 |
| set comprehension | 集合推导式 | {表达式 for ...},构建集合并去重 |
| generator expression | 生成器表达式 | (表达式 for ...),产出生成器,延迟计算、一次性 |
| generator | 生成器 | 逐个产出值的迭代器;用尽即空 |
range |
— | 3.x 中是不可变整数序列类型;包左不包右 |
zip() |
— | 多序列并行迭代配对成元组,长度取最短,一次性 |
| O(1) / O(n) | Order of 复杂度 | 常数时间 / 线性时间;用于讨论循环与容器操作开销 |
5 系列导航
6 参考链接
- Python 3 官方文档(中文)
- 更多流程控制工具 — if / for / else / break / continue
range类型文档zip()内置函数文档- 生成器表达式与推导式 — 数据结构章节
- PEP 572 — 赋值表达式(海象运算符)
- PEP 530 — 异步推导式(推导式语法规范)
- Time complexity(CPython 各容器操作复杂度)
分类:Python

浙公网安备 33010602011771号