第 13 课 · 入门 · 18 分钟

Python 列表推导式

用更紧凑的表达式从旧集合构建新集合,而不是写一长串循环。

列表推导式筛选字典推导式

小黑板要给每款饮料都加上五毛钱的燕麦奶升级。循环能做,一行表达式读起来却像 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 运行时,可能需要几秒钟。

浏览器内 Python本地运行 · 无需账号
首次运行会从 CDN 下载 Pyodide。修改后点击“运行代码”。

练习本课

12 道题,作答后立即看到解释。

查看 12 道题答案开始练习
← 元组与集合排序与 key →