8.12 列表解析
列表解析( List comprehensions, 或缩略为 list comps ) 来自函数式编程语言 Haskell . 它
是一个非常有用, 简单, 而且灵活的工具, 可以用来动态地创建列表. 它在 Python 2.0 中被加入.
在第 11 章, 函数中, 我们将讨论 Python 早就支持的函数式编程特性, 例如 lambda , map() ,
以及 filter() 等, 这些存在于 Python 中已经很长时间了, 但通过列表解析 , 它们可以被简化
为一个列表解析式子. map() 对所有的列表成员应用一个操作, filter() 基于一个条件表达式过
滤列表成员. 最后, lambda 允许你快速地创建只有一行的函数对象. 你不需要现在就去掌握这些,
在本节中你将看到它们出现在例子里, 因为我们需要讨论列表解析的优势. 首先让我们看看列表
解析的语法:
[expr for iter_var in iterable]
这个语句的核心是 for 循环, 它迭代 iterable 对象的所有条目. 前边的 expr 应用于序列
的每个成员, 最后的结果值是该表达式产生的列表. 迭代变量并不需要是表达式的一部分.
这里用到了第 11 章的一些代码. 它有一个计算序列成员的平方的 lambda 函数表达式:
map(lambda x: x ** 2, range(6))
[0, 1, 4, 9, 16, 25]
我们可以使用下面这样的列表解析来替换它:
[x ** 2 for x in range(6)]
[0, 1, 4, 9, 16, 25]
在新语句中, 只有一次函数调用( range() ), 而先前的语句中有三次函数调用(range() ,
map() , 以及 lambda ). 你也可以用括号包住表达式, 象 [(x ** 2) for x in range(6)] 这样, 更
Edit By Vheavens
Edit By Vheavens
便于阅读. 列表解析的表达式可以取代内建的 map() 函数以及 lambda , 而且效率更高. 结合 if
语句,列表解析还提供了一个扩展版本的语法:
[expr for iter_var in iterable if cond_expr]
这个语法在迭代时会过滤/捕获满足条件表达式 cond_expr 的序列成员.
回想下 odd() 函数, 它用于判断一个数值对象是奇数还是偶数(奇数返回 1 , 偶数返回 0 ):
def odd(n):
return n % 2
我们可以借用这个函数的核心操作, 使用 filter() 和 lambda 挑选出序列中的奇数:
seq = [11, 10, 9, 9, 10, 10, 9, 8, 23, 9, 7, 18, 12, 11, 12]
filter(lambda x: x % 2, seq)
[11, 9, 9, 9, 23, 9, 7, 11]
和先前的例子一样, 即使不用 filter() 和 lambda,我们同样可以使用列表解析来完成操作,
获得想要的数字:
[x for x in seq if x % 2]
[11, 9, 9, 9, 23, 9, 7, 11]
我们使用更多实用的例子结束这节.
===矩阵样例===
你需要迭代一个有三行五列的矩阵么? 很简单:
[(x+1,y+1) for x in range(3) for y in range(5)]
[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 1), (2, 2), (2,
3), (2, 4), (2, 5), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5)]
===磁盘文件样例===
假设我们有如下这样一个数据文件 hhga.txt , 需要计算出所有非空白字符的数目:
And the Lord spake, saying, “First shalt thou take
out the Holy Pin. Then shalt thou count to three,
Edit By Vheavens
Edit By Vheavens
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, nei- ther 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 )为单词, 然后我们可以像这样计算单词个数:
f = open(‘hhga.txt’, ‘r’)
len([word for line in f for word in line.split()])
91
快速地计算文件大小
import os
os.stat(‘hhga.txt’).st_size
499L
假定文件中至少有一个空白字符, 我们知道文件中有少于 499 个非空字符. 我们可以把每个
单词的长度加起来, 得到和.
f.seek(0)
sum([len(word) for line in f for word in line.split()])
408
这里我们用 seek() 函数回到文件的开头, 因为迭代器已经访问完了文件的所有行. 一个清晰
明了的列表解析完成了之前需要许多行代码才能完成的工作! 如你所见, 列表解析支持多重嵌套
for 循环以及多个 if 子句. 完整的语法可以在官方文档中找到. 你也可以在 PEP 202 中找到更多
关于列表解析的资料.