Python Cookbook-4.2 通过列表推导构建列表
任务
你想通过操作和处理一个序列(或其他的可迭代对象)中的元素来创建一个新的列表。
解决方案
假设你想通过给某个列表中的每个元素都加上23 来构建一个新列表。用列表推导可以很直接地实现这个想法:
thenewlist = [x + 23 for x in theoldlist]
同样,假设需要用某列表中的所有大于5的元素来构成一个新列表。用列表推导代码可以写成这样:
thenewlist = x for x in theoldlist if x>5]
当你试图将这两种想法合二为一的时候,可以增加一个if子句,并对选定的项使用某些表达式,比如加上23,这些都可以用一行概括成:
thenewlist = [x + 23 for x in theoldlist if x>5]
讨论
优雅、清晰和务实,都是 Python的核心价值观,列表推导说明了这三点是怎样和谐地统一起来的。事实上,当你直觉地考虑“改变某列表”而不是新建某列表时,列表推导常常是最好的办法。比如,假如需要将某列表L中的所有大于100的元素设置成100,最好的方法是:
L[:] = [min(x,100) for x in L]
上面代码在给一个“整个列表的切片”赋值的同时,修改了该列表对位的数据,而不是试图对名字L重新绑定,比如写成L=.…。
当你只是需要执行一个循环的时候,不应该使用列表推导。如果需要循环,那就写相应的循环代码。关于循环列表的例子,参看4.4节。第19章还会有更多的有关 Python的迭代的内容。
另外,如果 Python 有内建的操作或者类型能够以更直接的方式实现你的需求,你也不要使用列表推导。比如,为了复制一个列表,用L1=list(L)就好,不要这么用:
L1 =[x for x in L]
类似地,如果想对每个元素都调用一个函数,并使用函数的返回结果,应该用L1=map(f,L),而不是L1=[f(x) for x in L]。不过大多数情况下,这种列表推导用法也没问题。
在 Python 2.4中,当序列过长,而你每次只需要获取一个元素的时候,应当考虑使用生成器表达式,而不是列表推导。生成器表达式的语法和列表推导一样,只不过生成器表达式是被()圆括号括起的,而不是方括号[]。比如,如果我们需要计算本节解决方案里的列表的某些元素之和,在Python 2.3中可以这么做:
total = sum([x + 23 for x in theoldlist if x > 5])
在 Python 2.4中,代码可以写得更自然一点,方括号可以省略掉(也不用增加多余的圆括号——有调用内建的sum 函数的圆括号就足够了):
total = sum(x+23 for x in theoldlist if x>5)
除了更加简洁,这个方法还可以避免一次性将整个列表载入内存,从而在列表特别长的时候,在一定程度上提高处理速度。