Skip to content

第4章 流程控制

学习目标

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

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

4.1 条件语句

4.1.1 if语句

python
age = 18
if age >= 18:
    print("成年人")

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

4.1.2 if-else语句

python
age = 16
if age >= 18:
    print("成年人")
else:
    print("未成年人")

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

4.1.3 if-elif-else语句

python
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 条件表达式(三元运算符)

python
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 逻辑运算符组合条件

python
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最重要的新特性之一:

python
# 字面量模式
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循环

python
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)

python
# 迭代可迭代对象
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块

python
# 查找质数
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 循环嵌套

python
# 九九乘法表
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——提前终止循环

python
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——跳过当前迭代

python
# 只打印奇数
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——空操作占位

python
# 占位:待实现
class MyError(Exception):
    pass

def todo_function():
    pass

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

4.4 推导式

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

4.4.1 列表推导式

python
# 基本形式:[表达式 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 字典推导式

python
# 基本形式:{键表达式: 值表达式 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 集合推导式

python
# 基本形式:{表达式 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 生成器表达式

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

python
# 列表推导式:立即计算,占用内存
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模块

python
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 实用迭代模式

python
# 反向迭代
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 经典算法实现

二分查找

python
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

冒泡排序

python
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]))

快速排序

python
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]))

斐波那契数列

python
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 循环优化技巧

避免在循环内重复计算

python
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}秒")

使用内置函数替代循环

python
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}秒")

提前退出优化

python
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 时间复杂度分析

python
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引入的模式匹配提供了强大的控制流表达能力:

python
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循环:

python
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 性能优化技术

python
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 循环展开与向量化

python
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章 函数与模块

青少年创意编程 - 高中Python组 - 江苏省宿城中等专业学校