讲解
推导式(comprehension)是用一行表达式从已有序列构建新容器的语法,是 Python 最具代表性的风格。列表推导式 [表达式 for 变量 in 可迭代对象] 等价于一个 for 循环加 append:[x * x for x in range(5)] 得到 [0, 1, 4, 9, 16]。加上过滤条件就是「变换 + 筛选」一体:[x for x in nums if x > 0]。它取代了大多数 map/filter 的场合——社区共识是推导式可读性更好,看到 map(lambda...) 基本都该改写成推导式。
同样的语法延伸到其他容器:字典推导 {k: v for ...} 从键值对构建字典,{v: k for k, v in d.items()} 一行实现键值互换;集合推导 {x % 10 for x in nums} 顺带完成去重;圆括号版本 (x for x in ...) 是上一章的生成器表达式,惰性不立即求值。嵌套循环也可以写:[row * col for row in range(1, 4) for col in range(1, 4)],后面的 for 是内层。
推导式的边界要清醒:一个表达式能写清才用,嵌套两层以上、带复杂条件逻辑的转换,老实写 for 循环更可读。还有条件表达式的位置别搞混:[f(x) if cond else g(x) for x in xs](if-else 在 for 前,是表达式的一部分)和 [f(x) for x in xs if cond](if 在 for 后,是过滤)。最后注意推导式有自己的作用域,循环变量不会泄漏到外面——这是 Python 3 相对 Python 2 的改进。
示例
# 列表推导:变换 + 筛选
nums = [1, 2, 3, 4, 5, 6]
print('平方:', [x * x for x in nums])
print('只要偶数:', [x for x in nums if x % 2 == 0])
print('偶数平方:', [x * x for x in nums if x % 2 == 0])
# 与等价的 for 循环对照
result = []
for x in nums:
if x % 2 == 0:
result.append(x * x)
print('for 写法结果相同:', result)
# 条件表达式(在 for 之前)vs 过滤条件(在 for 之后)
print('奇偶标签:', ['偶' if x % 2 == 0 else '奇' for x in nums])
# 字符串处理实战
words = [' Apple ', 'BANANA', ' cherry']
print('清洗:', [w.strip().lower() for w in words])
# 字典推导:键值互换、快速建表
scores = {'语文': 80, '数学': 90}
print('键值互换:', {v: k for k, v in scores.items()})
print('平方表:', {n: n * n for n in range(1, 6)})
# 集合推导:变换顺带人去重
print('个位数字集合:', sorted({x % 10 for x in [11, 22, 33, 44, 51]}))
# 嵌套循环:九九表的一角
print('乘法表片段:', [f'{i}x{j}={i * j}' for i in range(1, 4) for j in range(1, 4)])
# 生成器表达式做聚合,省一次列表
total = sum(len(line) for line in ['aa', 'bbb', 'c'])
print('总字符数:', total)
# 循环变量不泄漏
_ = [temp for temp in range(3)]
try:
print(temp)
except NameError:
print('推导式的循环变量不泄漏到外部')
常见坑
- if 位置写错:过滤条件在 for 之后([x for x in xs if ok]),条件表达式在 for 之前([a if ok else b for x in xs])。混了会报语法错误或逻辑错误。
- 推导式里塞副作用:[print(x) for x in xs] 造出一列表 None 只为打印——副作用请用普通 for 循环。
- 三层嵌套炫技:[f(x, y, z) for x in a for y in b for z in c if ...] 没人读得懂。超过一层嵌套就展开成循环。
- 需要惰性却用了列表推导:处理大文件 [process(line) for line in f] 一次性全读进内存,换圆括号 (process(line) for line in f) 逐行处理。
小结
[表达式 for 变量 in 序列 if 条件] 一行完成变换筛选;字典、集合、生成器各有推导形态;复杂逻辑回 for 循环。下一章给函数加外挂:装饰器。