小黑板要给每款饮料都加上五毛钱的燕麦奶升级。循环能做,一行表达式读起来却像 Maya 会脱口而出的那句话。什么时候推导式才是更清楚的选择?
推导式可以用一个表达式,从可迭代对象创建新的列表、集合或字典。它不一定比普通循环更好;但当转换很短、结果就是唯一目标时,推导式通常更清楚。 你还会看到推导式的作用域、嵌套 for 的阅读顺序,以及只能消费一次的生成器。
转换每个元素
for 前面的表达式会应用到每个元素。结果是新列表,原列表不变。表达式要短到读者一眼能看出输入和输出。
nums = [1, 2, 3]
doubled = [n * 2 for n in nums]
print(doubled)映射值
用 if 筛选
末尾的 if 子句只保留通过测试的值。条件要放在 for 子句之后。如果需要多个分支,普通循环通常更好读。
nums = [1, 2, 3, 4]
even = [n for n in nums if n % 2 == 0]
print(even)筛选值
构建字典
字典推导式用冒号分隔键表达式和值表达式。需要同时拿到原键和值时,遍历 items()。
prices = {"latte": 4.5, "tea": 3.0}
expensive = {name: price for name, price in prices.items() if price >= 4}
print(expensive)筛选字典
推导式的变量留在局部
Python 3 的推导式有自己的作用域。外面的 n 不会读到推导式最后一次的值,这能避免变量泄漏。能带出推导式的只有结果集合本身。
n = 99
squares = [n * n for n in range(4)]
print(squares)
print(n)循环变量不会泄漏到外面
嵌套 for 从左往右读
嵌套推导式就是把两层循环压成一行。第一个 for 是外层,第二个 for 对每个外层元素运行。筛选条件放在它所筛选的循环之后;顺序不明显时,改用具名循环。
rows = [[1, 2], [3, 4]]
flat = [value for row in rows for value in row]
pairs = [(row, value) for row in rows for value in row if value % 2 == 0]
print(flat)
print(pairs)外层在前,筛选在后
生成器表达式只能消费一次
圆括号创建惰性生成器。sum() 会消费它;第二次再遍历,拿到的是已经耗尽的迭代器,所以结果是 0。需要反复遍历就用列表,或者重新构造生成器。
numbers = (n * n for n in range(4))
print(sum(numbers))
print(sum(numbers))
print(list(n * n for n in range(4)))流一次,或者把值存下来
本课要点
- 推导式返回一个新的集合。
- for 子句提供值,if 子句负责筛选。
- 字典推导式在 for 前使用键: 值。
- 逻辑变得难读时,改用普通循环。
- 生成器只流一次;需要反复遍历时用列表。
常见错误
- 把 if 筛选写在 for 子句前面。
- 只用推导式产生 print 等副作用。
- 写出没人能读懂的复杂嵌套推导式。
- 把同一个生成器交给两个消费者,却期待两边都看到全部元素。
为 1 到 5 构建平方数列表,再只保留大于 10 的平方数。
动手试试
改改变量、先预测输出,再运行代码。第一次点击“运行代码”时会下载 Python 运行时,可能需要几秒钟。
练习本课
12 道题,作答后立即看到解释。