第4章 流程控制
学习目标
完成本章学习后,读者将能够:
- 掌握条件语句的完整语法体系,包括模式匹配(match-case)
- 理解Python循环机制的底层原理与迭代协议
- 熟练运用推导式(Comprehension)编写简洁高效的数据转换代码
- 掌握break、continue、else子句的精确语义
- 理解生成器表达式的惰性求值特性及其内存优势
4.1 条件语句
4.1.1 if语句
age = 18
if age >= 18:
print("成年人")
score = 85
if score >= 60:
print("及格")
print("恭喜!")4.1.2 if-else语句
age = 16
if age >= 18:
print("成年人")
else:
print("未成年人")
number = 7
print("偶数" if number % 2 == 0 else "奇数")4.1.3 if-elif-else语句
score = 85
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 70:
grade = "C"
elif score >= 60:
grade = "D"
else:
grade = "F"
print(f"成绩等级:{grade}")学术注记:Python的
elif是else if的缩写,避免了C/Java中深层嵌套的}链。Python没有switch-case语句(3.10之前),传统上使用if-elif链或字典分派模式替代。
4.1.4 条件表达式(三元运算符)
age = 20
status = "成年人" if age >= 18 else "未成年人"
x, y = 10, 20
max_value = x if x > y else y
# 链式条件表达式(可读性差,不推荐超过两层)
score = 85
grade = (
"A" if score >= 90 else
"B" if score >= 80 else
"C" if score >= 70 else
"D" if score >= 60 else
"F"
)4.1.5 逻辑运算符组合条件
age = 25
has_license = True
if age >= 18 and has_license:
print("可以驾驶")
if age < 18 or not has_license:
print("不能驾驶")
# Pythonic:链式比较
if 18 <= age <= 30:
print("年龄在18到30之间")
# 成员测试
if char in "aeiou":
print("元音字母")4.1.6 模式匹配(Python 3.10+)
PEP 634引入的结构模式匹配是Python 3.10最重要的新特性之一:
# 字面量模式
def http_status(status: int) -> str:
match status:
case 200:
return "OK"
case 301:
return "Moved Permanently"
case 400:
return "Bad Request"
case 404:
return "Not Found"
case 500:
return "Internal Server Error"
case _:
return "Unknown Status"
# 序列模式——解构与捕获
def process_command(command: str) -> str:
match command.split():
case ["quit"]:
return "退出程序"
case ["hello", name]:
return f"你好,{name}!"
case ["add", x, y]:
return f"结果:{int(x) + int(y)}"
case ["list", *items]:
return f"列表:{items}"
case _:
return "未知命令"
# 映射模式——字典解构
def handle_event(event: dict) -> str:
match event:
case {"type": "click", "x": x, "y": y}:
return f"点击位置:({x}, {y})"
case {"type": "key", "key": key}:
return f"按键:{key}"
case {"type": "scroll", "delta": delta} if delta > 0:
return "向上滚动"
case {"type": "scroll"}:
return "向下滚动"
case _:
return "未知事件"
# 类模式——对象解构
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
def describe_point(point: Point) -> str:
match point:
case Point(x=0, y=0):
return "原点"
case Point(x=0, y=y):
return f"Y轴上,y={y}"
case Point(x=x, y=0):
return f"X轴上,x={x}"
case Point(x=x, y=y):
return f"坐标({x}, {y})"
# OR模式与守卫条件
def classify(value: int) -> str:
match value:
case 0 | 1:
return "二进制位"
case n if n < 0:
return "负数"
case n if n > 100:
return "大数"
case _:
return "普通正数"学术注记:模式匹配源自函数式编程语言(Haskell、Scala、Rust),其核心思想是声明式的——描述"数据的形状"而非"如何提取数据"。相比
if-elif链,模式匹配将数据解构与条件判断合二为一,显著减少了样板代码。
4.2 循环语句
4.2.1 while循环
count = 0
while count < 5:
print(count)
count += 1
# 经典算法:数字反转
number = 12345
reversed_num = 0
while number > 0:
digit = number % 10
reversed_num = reversed_num * 10 + digit
number //= 10
print(f"反转:{reversed_num}") # 54321
# 欧几里得算法:最大公约数
def gcd(a: int, b: int) -> int:
while b:
a, b = b, a % b
return a4.2.2 for循环与迭代协议
Python的for循环不基于索引,而是基于迭代协议(Iterator Protocol):
# 迭代可迭代对象
for fruit in ["apple", "banana", "cherry"]:
print(fruit)
for char in "Python":
print(char)
for key, value in {"name": "Alice", "age": 25}.items():
print(f"{key}: {value}")
# range详解
for i in range(5): # 0, 1, 2, 3, 4
print(i)
for i in range(1, 6): # 1, 2, 3, 4, 5
print(i)
for i in range(0, 10, 2): # 0, 2, 4, 6, 8
print(i)
for i in range(10, 0, -1): # 10, 9, 8, ..., 1
print(i)
# enumerate:带索引迭代
fruits = ["apple", "banana", "cherry"]
for index, fruit in enumerate(fruits, start=1):
print(f"{index}: {fruit}")
# zip:并行迭代
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
for name, age in zip(names, ages):
print(f"{name}, {age}岁")
# zip不等长时以最短为准
from itertools import zip_longest
for item in zip_longest([1, 2, 3], [4, 5], fillvalue=0):
print(item) # (1, 4), (2, 5), (3, 0)学术注记:Python的
for循环本质是调用iter()获取迭代器,然后反复调用__next__()直到StopIteration异常。这意味着任何实现了__iter__()和__next__()的对象都可以用于for循环。range对象是惰性求值的,不会一次性生成所有数字,因此range(10**9)不会消耗大量内存。
4.2.3 循环的else子句
Python循环支持else子句,其语义是:循环正常结束(未被break中断)时执行else块:
# 查找质数
def is_prime(n: int) -> bool:
if n < 2:
return False
for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
break
else:
return True
return False
# 等价写法(更易读)
def is_prime_v2(n: int) -> bool:
if n < 2:
return False
for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
return False
return True工程实践:循环的
else子句是Python中容易混淆的语法特性。在简单场景中,使用return提前退出往往比for-else更清晰。for-else最适合的场景是"查找-未找到"模式。
4.2.4 循环嵌套
# 九九乘法表
for i in range(1, 10):
for j in range(1, i + 1):
print(f"{j}×{i}={i*j}", end=" ")
print()
# 矩阵遍历
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
for row in matrix:
for item in row:
print(item, end=" ")
print()
# 对角线遍历
for i in range(len(matrix)):
print(matrix[i][i], end=" ")
print()4.3 循环控制
4.3.1 break——提前终止循环
for i in range(10):
if i == 5:
break
print(i) # 0, 1, 2, 3, 4
# 实用:查找第一个满足条件的元素
def find_first_even(numbers: list) -> int | None:
for num in numbers:
if num % 2 == 0:
return num
return None
# 注意:break只跳出最内层循环
for i in range(3):
for j in range(3):
if i == 1 and j == 1:
break # 只跳出内层循环
print(f"({i}, {j})")4.3.2 continue——跳过当前迭代
# 只打印奇数
for i in range(10):
if i % 2 == 0:
continue
print(i) # 1, 3, 5, 7, 9
# 过滤无效数据
records = [{"name": "Alice", "age": 25}, None, {"name": "Bob", "age": 30}]
for record in records:
if record is None:
continue
print(f"{record['name']}: {record['age']}岁")4.3.3 pass——空操作占位
# 占位:待实现
class MyError(Exception):
pass
def todo_function():
pass
# pass vs ...(Ellipsis)
def todo_function():
... # 等价于pass,某些团队偏好此写法4.4 推导式
推导式(Comprehension)是Python最具特色的语法之一,将循环与条件判断浓缩为简洁的表达式。
4.4.1 列表推导式
# 基本形式:[表达式 for 变量 in 可迭代对象]
squares = [x ** 2 for x in range(10)]
print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 带条件过滤:[表达式 for 变量 in 可迭代对象 if 条件]
evens = [x for x in range(20) if x % 2 == 0]
# 带条件表达式:[表达式1 if 条件 else 表达式2 for 变量 in 可迭代对象]
labels = ["偶数" if x % 2 == 0 else "奇数" for x in range(5)]
# 嵌套循环
pairs = [(x, y) for x in range(3) for y in range(3)]
# 多层推导式(矩阵转置)
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposed = [[row[i] for row in matrix] for i in range(3)]
# 扁平化嵌套列表
flattened = [num for row in matrix for num in row]4.4.2 字典推导式
# 基本形式:{键表达式: 值表达式 for 变量 in 可迭代对象}
squares = {x: x ** 2 for x in range(5)}
print(squares) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 从两个列表创建字典
keys = ["name", "age", "city"]
values = ["Alice", 25, "北京"]
person = {k: v for k, v in zip(keys, values)}
# 过滤
scores = {"Alice": 85, "Bob": 72, "Charlie": 92, "David": 65}
passed = {name: score for name, score in scores.items() if score >= 80}
# 字符频率统计
text = "hello world"
freq = {char: text.count(char) for char in set(text)}4.4.3 集合推导式
# 基本形式:{表达式 for 变量 in 可迭代对象}
squares = {x ** 2 for x in range(-5, 6)}
print(squares) # {0, 1, 4, 9, 16, 25} - 自动去重
# 提取唯一元音
text = "Hello World"
vowels = {char.lower() for char in text if char.lower() in "aeiou"}
print(vowels) # {'e', 'o'}4.4.4 生成器表达式
生成器表达式使用圆括号,惰性求值,不一次性生成所有结果:
# 列表推导式:立即计算,占用内存
squares_list = [x ** 2 for x in range(10)]
# 生成器表达式:惰性计算,节省内存
squares_gen = (x ** 2 for x in range(10))
print(type(squares_gen)) # <class 'generator'>
# 逐个获取值
print(next(squares_gen)) # 0
print(next(squares_gen)) # 1
# 配合聚合函数使用(最常见场景)
total = sum(x ** 2 for x in range(100))
maximum = max(x ** 2 for x in range(100))
has_any = any(x < 0 for x in [1, 2, 3])
all_positive = all(x > 0 for x in [1, 2, 3])
# 内存对比
import sys
list_comp = [x ** 2 for x in range(10000)]
gen_expr = (x ** 2 for x in range(10000))
print(sys.getsizeof(list_comp)) # ~87616 字节
print(sys.getsizeof(gen_expr)) # ~200 字节工程实践:当结果只需迭代一次(如传入
sum()、max()、any()等函数),优先使用生成器表达式而非列表推导式。当需要多次迭代或索引访问时,使用列表推导式。
4.5 迭代工具
4.5.1 itertools模块
from itertools import count, cycle, repeat, chain, islice, groupby, product, permutations, combinations
# 无限迭代器
for i in islice(count(10, 2), 5): # 从10开始,步长2,取5个
print(i) # 10, 12, 14, 16, 18
# 链接多个可迭代对象
list1 = [1, 2, 3]
list2 = ['a', 'b', 'c']
for item in chain(list1, list2):
print(item)
# 排列组合
print(list(permutations('ABC', 2))) # [('A','B'), ('A','C'), ('B','A'), ...]
print(list(combinations('ABC', 2))) # [('A','B'), ('A','C'), ('B','C')]
print(list(product('AB', '12'))) # [('A','1'), ('A','2'), ('B','1'), ('B','2')]
# 分组
data = [('A', 1), ('A', 2), ('B', 3), ('B', 4)]
for key, group in groupby(data, key=lambda x: x[0]):
print(f"{key}: {list(group)}")4.5.2 实用迭代模式
# 反向迭代
for i in reversed(range(5)):
print(i) # 4, 3, 2, 1, 0
# 排序迭代
students = [{"name": "Alice", "score": 85}, {"name": "Bob", "score": 92}]
for s in sorted(students, key=lambda x: x["score"], reverse=True):
print(s)
# 字典安全迭代中修改
d = {"a": 1, "b": 2, "c": 3}
for key in list(d.keys()):
if d[key] < 2:
del d[key]4.6 算法思维训练
4.6.1 经典算法实现
二分查找
def binary_search(arr: list, target: int) -> int | None:
"""二分查找:O(log n)时间复杂度"""
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return None
sorted_list = [1, 3, 5, 7, 9, 11, 13, 15]
print(binary_search(sorted_list, 7)) # 3
print(binary_search(sorted_list, 8)) # None冒泡排序
def bubble_sort(arr: list) -> list:
"""冒泡排序:O(n²)时间复杂度"""
n = len(arr)
for i in range(n):
swapped = False
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
swapped = True
if not swapped:
break
return arr
print(bubble_sort([64, 34, 25, 12, 22, 11, 90]))快速排序
def quick_sort(arr: list) -> list:
"""快速排序:平均O(n log n)"""
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quick_sort(left) + middle + quick_sort(right)
print(quick_sort([3, 6, 8, 10, 1, 2, 1]))斐波那契数列
def fibonacci_iterative(n: int) -> list:
"""迭代法:O(n)时间,O(n)空间"""
if n <= 0:
return []
if n == 1:
return [0]
fib = [0, 1]
for i in range(2, n):
fib.append(fib[i-1] + fib[i-2])
return fib
def fibonacci_generator():
"""生成器版本:无限序列"""
a, b = 0, 1
while True:
yield a
a, b = b, a + b
from itertools import islice
print(list(islice(fibonacci_generator(), 10)))4.6.2 循环优化技巧
避免在循环内重复计算
import time
def slow_version(n: int) -> list:
result = []
for i in range(n):
result.append(i * 3.14159 ** 2)
return result
def fast_version(n: int) -> list:
pi_squared = 3.14159 ** 2
return [i * pi_squared for i in range(n)]
n = 1_000_000
start = time.time()
slow_version(n)
print(f"慢版本: {time.time() - start:.3f}秒")
start = time.time()
fast_version(n)
print(f"快版本: {time.time() - start:.3f}秒")使用内置函数替代循环
import time
data = list(range(1_000_000))
start = time.time()
total = 0
for x in data:
total += x
print(f"循环求和: {time.time() - start:.4f}秒")
start = time.time()
total = sum(data)
print(f"内置sum: {time.time() - start:.4f}秒")
start = time.time()
squared = []
for x in data:
squared.append(x ** 2)
print(f"循环平方: {time.time() - start:.4f}秒")
start = time.time()
squared = [x ** 2 for x in data]
print(f"推导式: {time.time() - start:.4f}秒")提前退出优化
def find_target_slow(data: list, target: int) -> bool:
"""遍历全部元素"""
found = False
for item in data:
if item == target:
found = True
return found
def find_target_fast(data: list, target: int) -> bool:
"""找到即退出"""
for item in data:
if item == target:
return True
return False
def find_target_pythonic(data: list, target: int) -> bool:
"""Pythonic方式"""
return target in data4.6.3 时间复杂度分析
def analyze_complexity():
"""常见循环模式的时间复杂度"""
# O(n) - 单层循环
def o_n(n):
for i in range(n):
pass
# O(n²) - 嵌套循环
def o_n_squared(n):
for i in range(n):
for j in range(n):
pass
# O(log n) - 对数循环
def o_log_n(n):
i = 1
while i < n:
i *= 2
# O(n log n) - 线性对数
def o_n_log_n(n):
for i in range(n):
j = 1
while j < n:
j *= 2
# O(√n) - 平方根循环
def o_sqrt_n(n):
i = 1
while i * i < n:
i += 1
analyze_complexity()| 复杂度 | 名称 | 示例 | n=1000时操作数 |
|---|---|---|---|
| O(1) | 常数 | 数组索引 | 1 |
| O(log n) | 对数 | 二分查找 | 10 |
| O(√n) | 平方根 | 质数判断 | 32 |
| O(n) | 线性 | 单层循环 | 1000 |
| O(n log n) | 线性对数 | 快速排序 | 10000 |
| O(n²) | 平方 | 嵌套循环 | 1000000 |
| O(2ⁿ) | 指数 | 递归斐波那契 | 10³⁰¹ |
4.7 前沿技术动态
4.7.1 结构化模式匹配(PEP 634)
Python 3.10引入的模式匹配提供了强大的控制流表达能力:
def process_event(event: dict) -> str:
match event:
case {"type": "login", "user": str(name)}:
return f"User {name} logged in"
case {"type": "logout", "user": str(name), "reason": reason}:
return f"User {name} logged out: {reason}"
case {"type": "error", "code": code} if code >= 500:
return f"Server error: {code}"
case {"type": "error", "code": code}:
return f"Client error: {code}"
case _:
return "Unknown event"4.7.2 异步迭代与循环
现代Python支持异步迭代器和异步for循环:
import asyncio
async def async_count(n: int):
for i in range(n):
await asyncio.sleep(0.1)
yield i
async def main():
async for num in async_count(5):
print(num)
async for num in async_count(3):
if num == 1:
continue
print(f"Got: {num}")
asyncio.run(main())4.7.3 性能优化技术
import itertools
# 使用itertools替代显式循环
data = range(1000000)
sum_result = sum(data)
any_positive = any(x > 0 for x in data)
all_positive = all(x >= 0 for x in data)
first_ten = itertools.islice(data, 10)
chunked = iter(lambda: list(itertools.islice(data, 100)), [])4.7.4 循环展开与向量化
import numpy as np
data = list(range(1000000))
result_loop = [x * 2 + 1 for x in data]
arr = np.array(data)
result_vectorized = arr * 2 + 14.8 本章小结
本章系统介绍了Python流程控制的完整体系:
- 条件语句:if-elif-else、条件表达式、模式匹配(3.10+)
- 循环机制:while循环、for循环基于迭代协议、循环的else子句
- 循环控制:break终止、continue跳过、pass占位
- 推导式:列表、字典、集合推导式——声明式数据转换
- 生成器表达式:惰性求值,内存友好的迭代方式
- 迭代工具:enumerate、zip、itertools模块
- 算法思维:经典算法实现、循环优化、时间复杂度分析
4.8.1 流程控制选择指南
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 简单条件判断 | if-elif-else | 清晰直观 |
| 多分支字面量匹配 | match-case (3.10+) | 声明式,支持解构 |
| 已知次数循环 | for + range | 明确迭代范围 |
| 条件驱动循环 | while | 条件终止 |
| 数据转换 | 推导式 | 简洁高效 |
| 大数据聚合 | 生成器表达式 | 内存友好 |
| 查找后处理 | for-else | 未找到时执行 |
4.8.2 性能优化清单
- 优先使用内置函数:
sum()、max()、any()比循环快 - 推导式优于append循环:列表推导式比循环append快约30%
- 生成器处理大数据:避免一次性加载全部数据到内存
- 提前退出:找到目标后立即
break或return - 循环外提:将不变计算移出循环
- 避免重复计算:缓存循环中重复使用的值
4.9 练习题
基础题
编写程序,判断一个年份是否为闰年(能被4整除但不能被100整除,或能被400整除)。
使用for循环打印九九乘法表,格式整齐。
使用列表推导式生成1-100中所有能被3整除但不能被5整除的数。
进阶题
找出100-999之间的所有水仙花数(各位数字的立方和等于该数本身),如153=1³+5³+3³。
使用模式匹配实现一个简易计算器,支持
add、sub、mul、div命令。编写函数,使用生成器表达式实现一个内存高效的大文件行过滤器。
项目实践
- 学生成绩管理系统:编写一个命令行程序,要求:
- 使用字典存储学生姓名和成绩
- 支持添加、删除、查询、统计操作
- 使用match-case处理命令
- 使用推导式进行数据过滤和统计
- 支持按成绩排序输出
思考题
Python的
for循环与C语言的for循环在语义上有何本质区别?迭代协议带来了哪些优势?列表推导式
[x for x in range(n) if condition]与等价的for循环在性能上有差异吗?为什么?循环的
else子句为什么被认为是Python中"令人困惑"的特性?在实际项目中,你倾向于使用for-else还是替代方案?
4.10 延伸阅读
4.10.1 算法与数据结构
- 《算法导论》 (CLRS) — 算法领域的权威教材,深入讲解时间复杂度分析和经典算法
- 《算法(第4版)》 (Robert Sedgewick) — Java实现但算法思想通用,配图丰富易于理解
- LeetCode (https://leetcode.cn/) — 在线算法练习平台,通过实战提升算法能力
- VisuAlgo (https://visualgo.net/) — 算法可视化工具,帮助理解算法执行过程
4.10.2 Python迭代机制
- PEP 255 — Simple Generators (https://peps.python.org/pep-0255/) — 生成器的原始提案
- PEP 634 — Structural Pattern Matching (https://peps.python.org/pep-0634/) — 模式匹配的官方规范
- itertools模块文档 (https://docs.python.org/3/library/itertools.html) — Python迭代工具的完整参考
- 《Fluent Python》第14章 — 深入讲解可迭代对象、迭代器和生成器
4.10.3 性能优化
- 《High Performance Python》 (Micha Gorelick & Ian Ozsvald) — Python性能优化的权威指南
- Python性能分析工具 —
cProfile、line_profiler、memory_profiler - TimeComplexity — Python Wiki (https://wiki.python.org/moin/TimeComplexity) — Python内置操作的时间复杂度参考
4.10.4 函数式编程
- 《Functional Programming in Python》 (David Mertz) — Python中的函数式编程技术
- fn.py (https://github.com/kachayev/fn.py) — Python函数式编程增强库
- toolz (https://github.com/pytoolz/toolz) — 函数式编程工具集,提供高效的迭代器操作
下一章:第5章 函数与模块