列表推导是构建列表的快捷方式。很多Python程序员都把列表推导 (list comprehension)简称为listcomps。
- 可读性
举例来说,例1把一个字符串变成Unicode码位的列表
symbols = '$¢£¥€¤'
codes = []
for symbol in symbols:
codes.append(ord(symbol))
>>> codes
[36, 162, 163, 165, 8364, 164]
例2把字符串变成Unicode码位的另外一种写法
symbols = '$¢£¥€¤'
codes = [ord(symbol) for symbol in symbols]
>>> codes
[36, 162, 163, 165, 8364, 164]
相比于例1,例2代码的功能从字面上就能轻松地看出来。
列表推导也可能被滥用。通常的原则是,只用列表推导来创建新的列表,并且尽量保持简短。
- 同filter和map的比较
列表推导可以帮助我们把一个序列或是其他可迭代类型中的元素过滤或是加工,然后再新建一个列表。Python内置的filter和map函数组合起来也能达到这一效果,但是可读性上打了不小的折扣。
下面来看看例子:
symbols = '$¢£¥€¤'
beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]
>>> beyond_ascii
[162, 163, 165, 8364, 164]
beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))
>>> beyond_ascii
[162, 163, 165, 8364, 164]
下面对列表推导和filter/map的效率进行比较
import timeit
TIMES = 10000
SETUP = """
symbols = '$¢£¥€¤'
def non_ascii(c):
return c > 127
"""
def clock(label, cmd):
res = timeit.repeat(cmd, setup=SETUP, number=TIMES)
print(label, *('{:.3f}'.format(x) for x in res))
clock('listcomp :', '[ord(s) for s in symbols if ord(s) > 127]')
clock('listcomp + func :', '[ord(s) for s in symbols if non_ascii(ord(s))]')
clock('filter + lambda :', 'list(filter(lambda c: c > 127, map(ord, symbols)))')
clock('filter + func :', 'list(filter(non_ascii, map(ord, symbols)))')
listcomp : 0.011 0.011 0.014
listcomp + func : 0.014 0.015 0.014
filter + lambda : 0.012 0.012 0.012
filter + func : 0.012 0.011 0.011
- 变量泄露
Python 2.x中,在列表推导中for关键词之后的赋值操作可能会影响列表推导上下文中的同名变量。
x = 'my precious'
dummy = [x for x in 'ABC']
>>> dummy
['A', 'B', 'C']
>>> x
'C'
如你所见,x原本的值被取代了,但是这种情况在Python3中是不会出现的。
列表推导、生成器表达式,以及同它们很相似的集合推导和字典推导,在Python3中都有了自己的局部作用域,就像函数似的。表达式内部的变量和赋值只在局部起作用,表达式的上下文里的同名变量还可以被正常引用,局部变量并不会影响到它们。
Python3
x = 'ABC'
dummy = [ord(x) for x in x]
>>> x
'ABC'
>>> dummy
[65, 66, 67]
x的值被保留了,列表推导也创建了正确的列表。
参考: Fluent Python by Luciano Ramalho (O’Reilly). Copyright 2015 Luciano Ramalho, 978-1-491-94600-8.
扫描二维码关注公众号,回复:
3087801 查看本文章