Menu

Python 列表推导式:语法、过滤与字典推导式

列表推导式让你用一行易读的代码构建新列表,可以映射、过滤或两者兼有,取代“循环加 append”的写法。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

一行代码,一个想法

这种写法你已经写过好几次了:先建一个空列表,遍历某个对象,也许做个过滤,然后把结果 append 进去。

列表推导式用一行代码表达同样的意思:

从左往右读:“一个新列表,由 numbers 中每个 n 对应的 n * 2 组成。”结构是 [expression for item in iterable]。

这并不是 Python 独有的技巧。它之所以叫“推导式”(comprehension),是因为你在声明式地描述新列表里有什么,而不是一步步写出构建它的过程。

加上过滤条件

在循环部分后面加一个 if 子句就能过滤:

第二个可以读作:“对 numbers 中的每个 n 取 n * n,但只在 n 是奇数时。”

等价的循环写法:

两种都没问题。推导式更短,而且读过几个之后,你会发现它其实更容易一眼看懂,因为意图就写在一行里。

同时映射和过滤

你可以一边用函数处理值,一边过滤:

对每个长度超过 3 个字母的单词,取它的大写形式。

推导式中的嵌套循环

两个 for 会生成类似笛卡尔积的组合:

顺序和嵌套循环时一样:第一个 for 是外层,第二个是内层。它的读法与等价的缩进循环一样,从左到右。

两层基本就是极限了,再多就不如普通循环好读。如果写到了三层,就改回循环吧。

字典推导式和集合推导式

思路一样,只是换了括号:

乍一看,集合推导式和字典推导式长得一模一样,区别在于前者是单个表达式,后者是 key: value。花括号加 : 就是字典;花括号里没有 : 就是集合。

生成器表达式

它和列表推导式几乎是双胞胎,只是用圆括号代替方括号,而且关键在于:它不会构建列表。

注意,我们直接把生成器传给了 sum() 和 any(),不需要额外的括号。当调用方只需要迭代一次时,生成器表达式就是合适的工具。对于大型集合,它比完整的列表更省内存。

什么时候该用普通循环

推导式很诱人。技术上你可以往一行里塞很多东西,但你不应该这么做。

在以下情况下,请改用普通循环:

  • 转换不止一步。如果需要中间变量,就把它完整写出来。
  • 有复杂的错误处理或分支。
  • 读这一行的人需要停下来不止一次才能看懂。

我遵循的规则是:如果我能一口气读完这个推导式并明白它在做什么,就保留它。如果读起来磕磕绊绊,就改写成循环。

有些推导式看起来很巧妙,实际上很伤人:

结果相同。循环版本是五行而不是一行,但“更长”并不等于“更差”。

几个会反复用到的模式

这五种模式能覆盖日常数据处理中相当多的工作。

下一步

你已经见过了主要的集合类型,以及把它们串起来的推导式。下一章:函数,把行为打包成有名字、可复用的单元。

常见问题

Python 列表推导式是什么?

一种从现有可迭代对象构建新列表的简洁语法。[x * 2 for x in numbers] 会创建一个新列表,其中每个数字都乘以 2。你还可以过滤:[x for x in numbers if x > 0]。

什么时候不应该使用列表推导式?

当它影响可读性的时候。如果里面的表达式很复杂或嵌套很深,用变量名清晰的普通 for 循环会更好懂。推导式适合简单的转换,也就是映射和过滤。更复杂的逻辑最好写成完整的循环。

列表推导式和生成器表达式有什么区别?

列表推导式会在内存中构建整个列表。生成器表达式(语法相同,只是用圆括号)每次只产出一个元素。当结果交给只迭代一次的东西时(比如 sum(...)),就用生成器,这样就不会创建一个马上就被丢掉的列表。

Coddy 编程语言插图

和 Coddy 一起学编程

开始