推导式是建立新容器的表达式,普通循环更适合多步骤副作用。生成器表达式则延迟生产,不是另一种已经填满的列表。
本节自测:筛出偶数并平方,输入空列表,再把列表推导式换生成器并消费两次。
预期结果与参考思路(先尝试再展开)
空输入得到空结果;生成器耗尽后再次消费为空,若需复用要重新创建或保存结果。
文档简介: 掌握 Python 条件与循环的全部写法,重点训练推导式这一 Python 标志性表达力工具,并学会 3.10+ 的 match-case 结构化分支
目标读者: 已掌握函数与类的开发者,想写出地道 Python 控制流的人
前置知识: 完成函数与类,理解可迭代对象概念
文档信息(用途、难度与维护记录)
| 属性 | 内容 |
|---|---|
| 模块 | 10-python-discovery |
| 象限 | 教程 |
| 难度 | ⭐ |
| 标签 | #控制流 #推导式 #match-case #迭代 |
| 更新日期 | 2026年9月 |
完成本文档后,你将能够:
- ✅ 用
if/elif/else与三元表达式组织分支逻辑 - ✅ 用
for/while配合enumerate、zip优雅遍历 - ✅ 写出列表/字典/集合/生成器四种推导式
- ✅ 用
match-case实现结构化模式匹配
score = 87
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B" # 注意 elif 而非 else if
else:
grade = "C"
# 三元表达式:值在前,条件在后
label = "及格" if score >= 60 else "不及格"
# 链式比较,数学式写法
if 0 <= score <= 100:
print("有效分数")match-case(3.10+)处理多分支结构与解构:
def handle(command: str) -> str:
match command.split():
case ["add" | "new", arg]: # 或模式
return f"添加 {arg}"
case ["rm", *targets]: # 序列解构模式
return f"删除 {len(targets)} 项"
case _:
return "未知命令"
⚠️ case _是通配符,必须放最后;匹配字面量直接写值,匹配变量会被当作捕获名——要用点号访问的常量(如Status.OK)才按值匹配。
def classify(command):
match command.split():
case ["add", name] if name:
return f"add:{name}"
case ["quit"]:
return "quit"
case _:
return "unknown"
found = None
for value in [0, -1, 4]:
if value > 0:
found = value
break
else:
found = "missing"
assert (classify("add Ada"), classify("quit"), classify("add"), found) == ("add:Ada", "quit", "unknown", 4)
print("python-keywords: match case guard; for else; break")该程序从正文原样提取并运行 match、case、if、for、break、else 与 return。
它只验证三条具体控制流路径;模式匹配的类模式、映射模式和异常传播需要另建输入验证。
Python 的 for 遍历可迭代对象,不是下标:
langs = ["python", "go", "rust"]
for lang in langs: # 直接遍历元素
print(lang)
for i, lang in enumerate(langs, start=1): # 需要下标时
print(f"{i}. {lang}")
for cn, en in zip(["一", "二"], [1, 2]): # 并行遍历
print(cn, en)
for k, v in {"py": 1991, "go": 2009}.items(): # 遍历字典
print(k, v)range 与循环控制:
for i in range(3): # 0, 1, 2(含头不含尾)
...
for i in range(10, 0, -2): # 10, 8, 6, 4, 2
...
while True: # while 适合"不确定次数"的场景
cmd = input("> ")
if cmd == "quit":
break # 跳出循环
if not cmd:
continue # 跳过本轮
print(cmd)for-else:循环未被 break 打断时执行 else,常用于搜索:
for item in items:
if item == target:
print("找到了")
break
else:
print("没找到") # 没被 break 才执行💡 优先考虑"遍历元素"而非"遍历下标再取元素"。需要下标就用
enumerate,这正是 Python 惯用法与 C 风格循环的分水岭。
推导式(comprehension)把"建列表的循环"压缩成一行;其性能常常不错,但是否更快取决于工作量、解释器和等价循环的具体写法:
nums = [1, 2, 3, 4, 5, 6]
# 列表推导式:[表达式 for 变量 in 可迭代 if 条件]
squares = [n * n for n in nums] # [1, 4, 9, 16, 25, 36]
evens = [n for n in nums if n % 2 == 0] # [2, 4, 6]
matrix_flat = [x for row in [[1, 2], [3, 4]] for x in row] # [1, 2, 3, 4]
# 字典推导式
len_map = {w: len(w) for w in ["py", "go"]} # {'py': 2, 'go': 2}
rev = {v: k for k, v in len_map.items()} # 键值互换
# 集合推导式
unique_len = {len(w) for w in ["py", "go", "rust"]} # {2, 4}
# 生成器表达式:圆括号,惰性求值,省内存
total = sum(n * n for n in range(1_000_000)) # 不建中间列表
⚠️ 推导式超过两层for或两个if就该换回普通循环——可读性优先,一行不是目的。
📖 四种推导式的语法对照、条件/嵌套写法与生成器惰性语义,见 推导式速查。
# 例1:清洗用户输入
raw = [" Ada ", "", "BOB", None, "cleo"]
names = [s.strip().title() for s in raw if s] # ['Ada', 'Bob', 'Cleo']
# 例2:条件表达式做分支映射
grades = {n: ("pass" if s >= 60 else "fail") for n, s in {"a": 70, "b": 50}.items()}
# 例3:展平嵌套 JSON 字段
records = [{"tags": ["web", "api"]}, {"tags": ["db"]}]
all_tags = {t for r in records for t in r["tags"]} # {'web', 'api', 'db'}any()/all() 配生成器表达式是集合判断的地道写法:
has_admin = any(u["role"] == "admin" for u in users)
all_valid = all(len(u["name"]) > 0 for u in users)需要索引和值时用 enumerate,需要键值时用 items,可使循环目的更明显;确实要按位置修改或访问邻居时,索引循环仍然合理。推导式适合短的变换,复杂条件和副作用用普通循环更易追踪。
生成器避免提前保存所有中间项,但只消费一次且最终聚合仍可能需要内存。for-else 表示循环未被 break 终止,不是“循环条件为假才开始执行”。用找到、未找到和空输入三个例子验证理解。
A: 新代码统一用推导式,可读性更好;map/filter 只在读老代码和已有函数可直接复用时考虑。
A: 远不止——它能解构序列、映射,配合守卫 if 子句做结构化匹配,本质是模式匹配而非值跳转。
- 给定
text = "the quick the lazy the dog",用推导式统计每个单词出现次数(dict 推导式 +.split()) - 用集合推导式输出出现超过一次的单词
- 把下面的 if 链改写成
match-case(含序列解构):
if cmd == "add":
op, *rest = args
elif cmd == "rm":
targets = args
else:
...- 为
add分支加守卫:args为空时输出提示
评估标准:两个练习均用推导式/match 完成,单函数不超过 10 行,ruff 通过。
- 📄 异常处理 — 控制流之外的错误流
- 📄 数据结构速查 — 推导式各目标的完整语法
- 📄 Python 一行式速查 — 推导式高频模式集