Python 编程教程 / 05 - 控制流
第 05 章:控制流
掌握 Python 的条件判断、循环、上下文管理器、模式匹配以及推导式。
5.1 条件判断
5.1.1 if 语句
temperature = 35
if temperature > 30:
print("天气炎热")
elif temperature > 20:
print("天气适宜")
elif temperature > 10:
print("天气凉爽")
else:
print("天气寒冷")
5.1.2 三元表达式
# 语法:value_if_true if condition else value_if_false
age = 20
status = "成年" if age >= 18 else "未成年"
# 嵌套三元(不推荐过度使用)
level = "高" if score >= 90 else ("中" if score >= 60 else "低")
5.1.3 match/case(Python 3.10+)
# 基本匹配
def http_status(code: int) -> str:
match code:
case 200:
return "OK"
case 301:
return "Moved Permanently"
case 404:
return "Not Found"
case 500:
return "Internal Server Error"
case _:
return f"Unknown: {code}"
# 模式匹配(解构)
def process_command(command: str) -> str:
match command.split():
case ["quit"]:
return "退出程序"
case ["load", filename]:
return f"加载文件: {filename}"
case ["copy", source, dest]:
return f"复制 {source} 到 {dest}"
case ["copy", *files]:
return f"复制多个文件: {files}"
case _:
return f"未知命令: {command}"
print(process_command("load data.csv")) # 加载文件: data.csv
print(process_command("copy a.txt b.txt")) # 复制 a.txt 到 b.txt
# 守卫条件
def check_number(n: int) -> str:
match n:
case x if x > 0:
return "正数"
case x if x < 0:
return "负数"
case _:
return "零"
5.1.4 match 与类模式
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
@dataclass
class Circle:
center: Point
radius: float
def describe_shape(shape):
match shape:
case Point(x=0, y=0):
return "原点"
case Point(x=x, y=y):
return f"点 ({x}, {y})"
case Circle(center=Point(x=cx, y=cy), radius=r):
return f"圆心 ({cx}, {cy}), 半径 {r}"
print(describe_shape(Point(0, 0))) # 原点
print(describe_shape(Point(1, 2))) # 点 (1, 2)
print(describe_shape(Circle(Point(0, 0), 5))) # 圆心 (0, 0), 半径 5
5.2 for 循环
5.2.1 基本迭代
# 遍历列表
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
# 遍历字符串
for char in "Python":
print(char, end=" ") # P y t h o n
# 遍历字典
person = {"name": "Alice", "age": 30}
for key, value in person.items():
print(f"{key}: {value}")
5.2.2 range()
# range(stop)
for i in range(5):
print(i) # 0, 1, 2, 3, 4
# range(start, stop)
for i in range(1, 6):
print(i) # 1, 2, 3, 4, 5
# range(start, stop, step)
for i in range(0, 10, 2):
print(i) # 0, 2, 4, 6, 8
# 倒序
for i in range(5, 0, -1):
print(i) # 5, 4, 3, 2, 1
5.2.3 enumerate()
# 同时获取索引和值
fruits = ["apple", "banana", "cherry"]
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
# 0: apple
# 1: banana
# 2: cherry
# 指定起始索引
for index, fruit in enumerate(fruits, start=1):
print(f"{index}. {fruit}")
5.2.4 zip()
# 并行遍历多个序列
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
cities = ["北京", "上海", "广州"]
for name, age, city in zip(names, ages, cities):
print(f"{name}, {age}岁, {city}")
# zip 以最短的为准
# itertools.zip_longest 以最长的为准
from itertools import zip_longest
for name, age in zip_longest(names, [25], fillvalue="未知"):
print(f"{name}: {age}")
5.2.5 for-else
# else 在循环正常结束时执行(未被 break 中断)
def find_factor(n: int) -> int | None:
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
print(f"{n} 的因子: {i}")
return i
else:
print(f"{n} 是质数")
return None
find_factor(15) # 15 的因子: 3
find_factor(17) # 17 是质数
5.3 while 循环
5.3.1 基本用法
# 基本 while
count = 0
while count < 5:
print(count)
count += 1
# 用户输入
while True:
user_input = input("输入 'quit' 退出: ")
if user_input == "quit":
break
print(f"你输入了: {user_input}")
5.3.2 while-else
# else 在循环条件为 False 时执行
n = 5
while n > 0:
print(n)
n -= 1
else:
print("倒计时结束!")
5.3.3 break 和 continue
# break:跳出整个循环
for i in range(10):
if i == 5:
break
print(i) # 0, 1, 2, 3, 4
# continue:跳过当前迭代
for i in range(10):
if i % 2 == 0:
continue
print(i) # 1, 3, 5, 7, 9
5.4 上下文管理器(with 语句)
# 文件操作
with open("data.txt", "w") as f:
f.write("Hello, World!")
# 多个上下文管理器
with open("input.txt") as fin, open("output.txt", "w") as fout:
fout.write(fin.read())
# 自定义上下文管理器
from contextlib import contextmanager
import time
@contextmanager
def timer(label: str):
start = time.perf_counter()
try:
yield
finally:
elapsed = time.perf_counter() - start
print(f"{label}: {elapsed:.4f} 秒")
with timer("数据处理"):
time.sleep(1)
# 执行某些操作
5.5 列表推导(List Comprehension)
5.5.1 基本语法
# 语法:[expression for item in iterable if condition]
# 平方
squares = [x**2 for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 带条件
evens = [x for x in range(20) if x % 2 == 0]
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
# 字符串处理
words = ["hello", "world", "python"]
upper_words = [w.upper() for w in words]
# ['HELLO', 'WORLD', 'PYTHON']
5.5.2 嵌套推导
# 展平二维列表
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in matrix for num in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 生成坐标对
points = [(x, y) for x in range(3) for y in range(3)]
# [(0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2)]
# 条件推导
result = [x if x > 0 else 0 for x in [-2, -1, 0, 1, 2]]
# [0, 0, 0, 1, 2]
5.5.3 对比 for 循环
# 使用 for 循环
result = []
for x in range(10):
if x % 2 == 0:
result.append(x ** 2)
# 使用列表推导(更简洁)
result = [x**2 for x in range(10) if x % 2 == 0]
5.6 字典推导和集合推导
# 字典推导
squares_dict = {x: x**2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 反转字典
original = {"a": 1, "b": 2, "c": 3}
reversed_dict = {v: k for k, v in original.items()}
# {1: "a", 2: "b", 3: "c"}
# 集合推导
unique_lengths = {len(word) for word in ["hello", "world", "hi", "hey"]}
# {2, 3, 5}
5.7 生成器表达式(Generator Expression)
# 语法类似列表推导,但用圆括号
gen = (x**2 for x in range(10))
print(type(gen)) # <class 'generator'>
# 惰性求值,节省内存
print(next(gen)) # 0
print(next(gen)) # 1
# 作为函数参数(可以省略额外的括号)
total = sum(x**2 for x in range(1_000_000))
max_val = max(x**2 for x in range(100))
# 对比:列表推导会立即创建整个列表,内存开销大
big_list = [x**2 for x in range(1_000_000)] # 内存大
big_gen = (x**2 for x in range(1_000_000)) # 几乎不占内存
| 特性 | 列表推导 | 生成器表达式 |
|---|---|---|
| 语法 | [...] | (...) |
| 返回类型 | list | generator |
| 内存 | 立即分配 | 惰性分配 |
| 速度 | 首次快 | 首次慢,后续快 |
| 重复使用 | ✅ 可以 | ❌ 不可以 |
5.8 常用迭代工具
5.8.1 itertools
import itertools
# chain:链接多个迭代器
combined = list(itertools.chain([1, 2], [3, 4], [5]))
# [1, 2, 3, 4, 5]
# product:笛卡尔积
colors = ["红", "蓝"]
sizes = ["S", "M", "L"]
for combo in itertools.product(colors, sizes):
print(combo)
# ('红', 'S'), ('红', 'M'), ('红', 'L'), ('蓝', 'S'), ...
# combinations:组合
print(list(itertools.combinations([1, 2, 3, 4], 2)))
# [(1,2), (1,3), (1,4), (2,3), (2,4), (3,4)]
# permutations:排列
print(list(itertools.permutations([1, 2, 3], 2)))
# [(1,2), (1,3), (2,1), (2,3), (3,1), (3,2)]
# groupby:分组
data = sorted(["apple", "avocado", "banana", "blueberry", "cherry"], key=lambda x: x[0])
for key, group in itertools.groupby(data, key=lambda x: x[0]):
print(f"{key}: {list(group)}")
# a: ['apple', 'avocado']
# b: ['banana', 'blueberry']
# c: ['cherry']
5.8.2 any() 和 all()
numbers = [2, 4, 6, 8, 10]
# any:至少一个为 True
print(any(x > 5 for x in numbers)) # True
print(any(x > 20 for x in numbers)) # False
# all:全部为 True
print(all(x % 2 == 0 for x in numbers)) # True
print(all(x > 5 for x in numbers)) # False
5.9 注意事项
🔴 注意:
for循环没有 C 风格的for(i=0; i<n; i++)语法,使用range()- 列表推导不要太复杂,超过两层嵌套应改用普通循环
- 生成器表达式只能迭代一次,重复使用需要重新创建
match/case是 Python 3.10+ 特性
💡 提示:
- 使用
enumerate()替代手动维护索引 - 使用
zip()并行遍历多个序列 - 优先使用推导式而非
map()+filter()(更 Pythonic) - 大数据集使用生成器表达式节省内存
📌 业务场景:
# 数据清洗:从 CSV 数据中提取有效记录
import csv
from datetime import datetime
def process_orders(orders: list[dict]) -> list[dict]:
"""处理订单数据。"""
# 筛选有效订单
valid = [
{
"id": order["id"],
"amount": float(order["amount"]),
"date": datetime.strptime(order["date"], "%Y-%m-%d"),
}
for order in orders
if order.get("status") == "completed"
and float(order.get("amount", 0)) > 0
]
# 按日期排序
valid.sort(key=lambda x: x["date"])
return valid
# 模拟数据
orders = [
{"id": 1, "amount": "100.50", "date": "2024-01-15", "status": "completed"},
{"id": 2, "amount": "0", "date": "2024-01-16", "status": "completed"},
{"id": 3, "amount": "75.00", "date": "2024-01-17", "status": "cancelled"},
]
result = process_orders(orders)
print(result) # [{'id': 1, 'amount': 100.5, 'date': datetime(...)}]
5.10 扩展阅读
- Compound Statements - Python 文档
- PEP 636 - Structural Pattern Matching
- itertools 模块文档
- 列表推导式指南
- 《流畅的 Python》第 2 章:序列构成的数组