第4章 流程控制

学习目标

完成本章学习后,读者将能够:

  • 掌握条件语句的完整语法体系,包括模式匹配(match-case)
  • 理解Python循环机制的底层原理与迭代协议
  • 熟练运用推导式(Comprehension)编写简洁高效的数据转换代码
  • 掌握break、continue、else子句的精确语义
  • 理解生成器表达式的惰性求值特性及其内存优势

4.1 条件语句

4.1.1 if语句

1
2
3
4
5
6
7
8
age = 18
if age >= 18:
print("成年人")

score = 85
if score >= 60:
print("及格")
print("恭喜!")

4.1.2 if-else语句

1
2
3
4
5
6
7
8
age = 16
if age >= 18:
print("成年人")
else:
print("未成年人")

number = 7
print("偶数" if number % 2 == 0 else "奇数")

4.1.3 if-elif-else语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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的elifelse if的缩写,避免了C/Java中深层嵌套的}链。Python没有switch-case语句(3.10之前),传统上使用if-elif链或字典分派模式替代。

4.1.4 条件表达式(三元运算符)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 逻辑运算符组合条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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最重要的新特性之一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# 字面量模式
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循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 a

4.2.2 for循环与迭代协议

Python的for循环不基于索引,而是基于迭代协议(Iterator Protocol)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 迭代可迭代对象
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块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 查找质数
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 循环嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 九九乘法表
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——提前终止循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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——跳过当前迭代

1
2
3
4
5
6
7
8
9
10
11
12
# 只打印奇数
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——空操作占位

1
2
3
4
5
6
7
8
9
10
# 占位:待实现
class MyError(Exception):
pass

def todo_function():
pass

# pass vs ...(Ellipsis)
def todo_function():
... # 等价于pass,某些团队偏好此写法

4.4 推导式

推导式(Comprehension)是Python最具特色的语法之一,将循环与条件判断浓缩为简洁的表达式。

4.4.1 列表推导式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 基本形式:[表达式 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 字典推导式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 基本形式:{键表达式: 值表达式 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 集合推导式

1
2
3
4
5
6
7
8
# 基本形式:{表达式 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 生成器表达式

生成器表达式使用圆括号,惰性求值,不一次性生成所有结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 列表推导式:立即计算,占用内存
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模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 实用迭代模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 反向迭代
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 经典算法实现

二分查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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

冒泡排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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]))

快速排序

1
2
3
4
5
6
7
8
9
10
11
12
13
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]))

斐波那契数列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 循环优化技巧

避免在循环内重复计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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}秒")

使用内置函数替代循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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}秒")

提前退出优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 data

4.6.3 时间复杂度分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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引入的模式匹配提供了强大的控制流表达能力:

1
2
3
4
5
6
7
8
9
10
11
12
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循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 性能优化技术

1
2
3
4
5
6
7
8
9
10
11
12
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 循环展开与向量化

1
2
3
4
5
6
7
8
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 + 1

4.8 本章小结

本章系统介绍了Python流程控制的完整体系:

  1. 条件语句:if-elif-else、条件表达式、模式匹配(3.10+)
  2. 循环机制:while循环、for循环基于迭代协议、循环的else子句
  3. 循环控制:break终止、continue跳过、pass占位
  4. 推导式:列表、字典、集合推导式——声明式数据转换
  5. 生成器表达式:惰性求值,内存友好的迭代方式
  6. 迭代工具:enumerate、zip、itertools模块
  7. 算法思维:经典算法实现、循环优化、时间复杂度分析

4.8.1 流程控制选择指南

场景推荐方案原因
简单条件判断if-elif-else清晰直观
多分支字面量匹配match-case (3.10+)声明式,支持解构
已知次数循环for + range明确迭代范围
条件驱动循环while条件终止
数据转换推导式简洁高效
大数据聚合生成器表达式内存友好
查找后处理for-else未找到时执行

4.8.2 性能优化清单

  1. 优先使用内置函数sum()max()any()比循环快
  2. 推导式优于append循环:列表推导式比循环append快约30%
  3. 生成器处理大数据:避免一次性加载全部数据到内存
  4. 提前退出:找到目标后立即breakreturn
  5. 循环外提:将不变计算移出循环
  6. 避免重复计算:缓存循环中重复使用的值

4.9 练习题

基础题

  1. 编写程序,判断一个年份是否为闰年(能被4整除但不能被100整除,或能被400整除)。

  2. 使用for循环打印九九乘法表,格式整齐。

  3. 使用列表推导式生成1-100中所有能被3整除但不能被5整除的数。

进阶题

  1. 找出100-999之间的所有水仙花数(各位数字的立方和等于该数本身),如153=1³+5³+3³。

  2. 使用模式匹配实现一个简易计算器,支持addsubmuldiv命令。

  3. 编写函数,使用生成器表达式实现一个内存高效的大文件行过滤器。

项目实践

  1. 学生成绩管理系统:编写一个命令行程序,要求:
    • 使用字典存储学生姓名和成绩
    • 支持添加、删除、查询、统计操作
    • 使用match-case处理命令
    • 使用推导式进行数据过滤和统计
    • 支持按成绩排序输出

思考题

  1. Python的for循环与C语言的for循环在语义上有何本质区别?迭代协议带来了哪些优势?

  2. 列表推导式[x for x in range(n) if condition]与等价的for循环在性能上有差异吗?为什么?

  3. 循环的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迭代机制

4.10.3 性能优化

  • 《High Performance Python》 (Micha Gorelick & Ian Ozsvald) — Python性能优化的权威指南
  • Python性能分析工具cProfileline_profilermemory_profiler
  • TimeComplexity — Python Wiki (https://wiki.python.org/moin/TimeComplexity) — Python内置操作的时间复杂度参考

4.10.4 函数式编程


下一章:第5章 函数与模块