8.12 列表解析

列表解析(List comprehensions,或缩略为list comps)来自函数式编程语言Haskell。它是一个非常有用、简单而且灵活的工具,可以用来动态地创建列表。它在Python 2.0中被加入。

在第11章中,我们将讨论Python早就支持的函数式编程特性,例如lambda、map()和filter()等,它们存在于Python中已经很长时间了,但通过列表解析,它们可以被简化为一个列表解析式子。map()对所有的列表成员应用一个操作,filter()基于一个条件表达式过滤列表成员。最后,lambda允许你快速地创建只有一行的函数对象。你不需要现在就去掌握这些,在本节中你将看到它们出现在例子里,因为我们需要讨论列表解析的优势。首先让我们看看列表解析的语法:

8.12 列表解析 - 图1

这个语句的核心是for循环,它迭代iterable对象的所有条目。前边的expr应用于序列的每个成员,最后的结果值是该表达式产生的列表。迭代变量并不需要是表达式的一部分。这里用到了第11章的一些代码。它有一个计算序列成员的平方的lambda函数表达式:

8.12 列表解析 - 图2

我们可以使用下面这样的列表解析来替换它:

8.12 列表解析 - 图3

在新语句中,只有一次函数调用(range()),而先前的语句中有三次函数调用(range()、map()和lambda)。你也可以用括号包住表达式,像[(x**2)for x in range(6)]这样,更便于阅读。列表解析的表达式可以取代内建的map()函数以及lambda,而且效率更高。结合if语句,列表解析还提供了一个扩展版本的语法:

8.12 列表解析 - 图4

这个语法在迭代时会过滤或“捕获”满足条件表达式cond_expr的序列成员。

回想一下odd()函数,它用于判断一个数值对象是奇数还是偶数(奇数返回1,偶数返回0):

8.12 列表解析 - 图5

我们可以借用这个函数的核心操作,使用filter()和lambda挑选出序列中的奇数:

8.12 列表解析 - 图6

和先前的例子一样,即使不用filter()和lambda,我们同样可以使用列表解析来完成操作,获得想要的数字:

8.12 列表解析 - 图7

我们使用更多实用的例子结束这节。

1. 矩阵样例

你需要迭代一个有3行5列的矩阵么?很简单:

8.12 列表解析 - 图8

2. 磁盘文件样例

假设我们有如下这样一个数据文件hhga.txt,需要计算出所有非空白字符的数目:

And the Lord spake, saying, “First shalt thou take out the Holy Pin. Then shalt thou count to three, no more, no less. Three shall be the number thou shalt count, and the number of the counting shall be three. Four shalt thou not count, neither count thou two, excepting that thou then proceed to three. Five is right out. Once the number three, being the third number, be reached, then lobbest thou thy Holy Hand Grenade of Antioch towards thy foe, who, being naughty in My sight, shall snuff it.”

我们已经知道可以通过for line in data迭代文件内容。不过,除了这个,我们还可以把每行分割(split)为单词,然后我们可以像这样计算单词个数:

8.12 列表解析 - 图9

快速地计算文件大小

8.12 列表解析 - 图10

假定文件中至少有一个空白字符,我们知道文件中有少于499个非空字符。我们可以把每个单词的长度加起来,得到和。

8.12 列表解析 - 图11

这里我们用seek()函数回到文件的开头,因为迭代器已经访问完了文件的所有行。一个清晰明了的列表解析完成了之前需要许多行代码才能完成的工作!如你所见,列表解析支持多重嵌套for循环以及多个if子句。完整的语法可以在官方文档中找到。你也可以在PEP 202中找到更多关于列表解析的资料。