>>>PyPathPython 学习站
首页›核心篇›kp-011
核心篇 · Core lv.1 入门 kp-011

推导式(comprehensions)

前置知识:kp-009、kp-010

1. 一句话定义

推导式是"由一个可迭代对象构造新容器"的声明式语法:[表达式 for 变量 in 可迭代 if 条件],覆盖 list/dict/set 三种形式与生成器表达式。

2. 为什么重要

它是 Pythonic 的第一标志:同一意图(过滤+映射)比等价 for 循环短一半且无临时变量;同时是通向生成器表达式(惰性)与 asyncio 数据流的自然入口。

3. 前置知识

kp-006(for 循环)、kp-009/kp-010(容器)。

4. 核心概念

python
squares = [x * x for x in range(10)]                    # 列表
evens   = [x for x in range(10) if x % 2 == 0]          # 带过滤
pairs   = {k: v for k, v in [("a",1),("b",2)]}          # 字典
uniq    = {c for c in "abracadabra"}                    # 集合
lazy    = (x * x for x in range(10))                    # 生成器表达式(惰性)

  • 条件位置两处不同:for x in it if cond 是过滤;[a if cond else b for x in it] 是三目变换。
  • 嵌套:从左到右读即外层到内层循环。

5. 原理与机制

等价展开规则:推导式 = "创建空容器 + for 循环填充"的编译糖(有自己的局部作用域,循环变量不泄漏)。

python
[x * 2 for x in xs if p(x)]
# ≈
out = []
for x in xs:
    if p(x):
        out.append(x * 2)

生成器表达式不立即求值,sum(x*x for x in big) 边算边耗,不构造中间列表——大数据下的关键差异。

6. 关键事实(模型/图示)

text
读法口诀:for 在前(数据从哪来),if 在后(留下谁),表达式最前(变成什么)
[expr for x in A for y in B if c]  ≡  for x in A: for y in B: if c: yield expr

7. 直观类比

for 循环是流水线上的工人一件件搬;推导式是开模:把"什么样的件、怎么变"描述成图纸,机器一次成型。图纸复杂到没人看得懂时,就该回去用流水线。

8. 实例与案例

python
# 过滤 + 排序组合:取出分数及格者的姓名并按分数倒序
names = sorted(
    (n for n, s in scores.items() if s >= 60),
    key=lambda n: -scores[n],
)

# 扁平化二维列表
flat = [x for row in matrix for x in row]

9. 常见误区

  1. 套超过两层 —— [... for x in A for y in B for z in C ...] 可读性崩坏,改普通循环。
  2. 生成器表达式当列表用第二次 —— 耗尽后为空;需要多次用列表推导式或重建。
  3. 在推导式里做副作用(写文件、print) —— 语义陷阱,用普通 for 表达意图。
  4. 以为推导式更快是"银弹" —— 常量级优势,大逻辑差异来自是否惰性,不是语法本身。

10. 自测题

  1. [x for x in range(10) if x % 2] 输出什么?
  2. sum(len(w) for w in words) 与 sum([len(w) for w in words]) 的差异?
  3. {x: x**2 for x in range(3)} 是什么?
参考答案
  1. [1, 3, 5, 7, 9](奇数)。
  2. 前者生成器表达式,逐个计算不建中间列表;后者先建完整列表再求和。
  3. 字典推导式,得 {0: 0, 1: 1, 2: 4}。

11. 与其他知识点的关系

  • kp-013 生成器:生成器表达式的完整机制。
  • kp-038 itertools:与推导式互补的工具箱。
  • kp-006 控制流:等价 for 循环。

12. 延伸阅读

  • Python 官方教程 §5.1.3 List Comprehensions