lang
lang/python/iterators/comprehension-style.md
medium
py-comprehension-style
列表/字典/集合推导式写法、海象 := 复用,以及推导式与生成器表达式的内存权衡。Use when 写推导式 / 选列表推导还是生成器 / 用海象避免重复计算。
推导式海象运算符comprehensionwalrus生成器表达式
paths
*.pypy/**/*.py
Python · 推导式风格
规则
| 你想 | 写法 |
|---|---|
| 由可迭代构造 list | [f(x) for x in it if cond(x)] |
| 构造 dict | {k: v for k, v in pairs} |
| 构造 set / 去重 | {f(x) for x in it} |
| 只遍历一次/喂给聚合 | (f(x) for x in it) 生成器表达式 |
| 推导内复用一次计算的结果 | 海象 :=:[y for x in it if (y := f(x)) > 0] |
选型:结果要多次用/索引/取 len → 列表推导;只遍历一次(sum/any/max/直接传函数)或结果集很大 → 生成器表达式,省内存。推导式只在逻辑简单时用——嵌套超过两层或带复杂分支,改写普通 for 更可读。
正例
# 列表推导:过滤 + 映射
active_ids = [u.id for u in users if u.is_active]
# 字典推导:反转映射
id_to_name = {u.id: u.name for u in users}
# 集合推导:去重
domains = {email.split("@")[1] for email in emails}
# 生成器表达式喂聚合:不建中间 list
total = sum(o.amount for o in orders if o.paid)
# 海象 := 复用计算结果,避免调用两次 expensive(x)
results = [y for x in data if (y := expensive(x)) is not None]
反例
# ❌ 推导式塞副作用(print/append/调接口),只为循环而推导
[print(x) for x in items] # 建了个没人用的 list,纯浪费
# ✅ 有副作用就老实写 for
for x in items:
print(x)
# ❌ 三层嵌套 + 多条件,没人读得懂
[f(x, y, z) for x in xs for y in ys if p(y) for z in zs if q(x, z)]
# ✅ 拆成普通 for + 生成器函数
# ❌ 大结果集用列表推导,峰值内存翻倍
data = [transform(row) for row in fetch_millions()]
total = sum(d.size for d in data)
# ✅ 一次性消费就用生成器表达式
total = sum(transform(row).size for row in fetch_millions())
理由:推导式的价值是「表达式构造容器」,塞副作用违背其语义且白建容器;嵌套过深时可读性比简洁更重要;只消费一次的大数据用生成器表达式避免物化。
自检
- [ ] 推导式里没有
print/append/ 接口调用等副作用? - [ ] 嵌套 ≤2 层、条件简单;复杂逻辑改回普通
for? - [ ] 只遍历一次或结果很大时用
(...)生成器表达式而非[...]? - [ ] 需要复用一次计算结果时用海象
:=,没有重复调用同一函数?