列表推导
列表推导(list comprehesion)可以从可迭代类型中的元素过滤或加工,构建出一个序列。
不用列表推导:
>>> symbols = 'abc123'
>>> codes = []
>>> for symbol in symbols:
... codes.append(ord(symbol))
...
>>> codes
[97, 98, 99, 49, 50, 51]
用列表推导:
>>> symbols = 'abc123'
>>> codes = [ord(symbol) for symbol in symbols]
>>> codes
[97, 98, 99, 49, 50, 51]
还可以用来生成两个或以上的可迭代类型的笛卡尔积,即两层或多层 for 循环的列表推导。
两层 for 循环的列表推导:
>>> colors = ['black', 'white']
>>> sizes = ['S', 'M', 'L']
>>> tshirts = [(color, size) for color in colors for size in sizes]
>>> tshirts
[('black', 'S'), ('black', 'M'), ('black', 'L'), ('white', 'S'), ('white', 'M'), ('white', 'L')]
相当于:
>>> for color in colors:
... for size in sizes:
... print((color, size))
...
('black', 'S')
('black', 'M')
('black', 'L')
('white', 'S')
('white', 'M')
('white', 'L')
需要注意的是,列表推导的前一个 for 循环就是外层 for 循环,后一个 for 循环就是内层 for 循环。
另外,python3.x 就不存在变量泄露的问题,列表推导内部的变量是局部的,仅限于列表推导内,同时也可以引用上下文的同名变量:
# python3.9
>>> x = 'ABC'
>>> dummy = [ord(x) for x in x]
>>> x
'ABC'
可以看到 x 变量并没有改变,如果是在 python2.x 中,它的值就被改变为 'C'。
# python2.7
>>> x = 'ABC'
>>> dummy = [ord(x) for x in x]
>>> x
'C'
使用列表推导的最大好处就是可读性更高。因此,一般建议两层 for 循环内使用列表推导。
生成器表达式
生成器表达式的语法跟列表推导类似,不同的是表达式外面用括号。但是,本质上的不同是生成器表达式遵守迭代器协议,逐个地产出元素,而不是一次性产出一个所有的数据,而列表推导先建立一个完整的列表,然后再把这个列表传递到某个构造函数里。
总的来说就是,生成器表达式比列表推导更节省内存。
>>> symbols = 'abc132'
>>> tuple(ord(symbol) for symbol in symbols) # 1
(97, 98, 99, 49, 51, 50)
>>>
>>> import array
>>> array.array('I', (ord(symbol) for symbol in symbols)) # 2
array('I', [97L, 98L, 99L, 49L, 51L, 50L])
如果生成器表达式是函数调用过程的唯一参数,那么可以省略外层的括号。
下面的例子比较,可以看出生成器表达式是在每次执行 for 循环时,才产出一个元素。
>>> s = 'apple'
>>> t = (ord(s) for s in s)
>>> for a in t:
... print(a)
...
97
112
112
108
101
>>> for a in t:
... print(a)
...
>>>
t 是一个生成器,第一次执行 for 循环遍历后,可以逐个输出 s 的字符,但是第二次执行 for 循环时,生成器 t 已经产生所有的元素,相当于指针已经指向 s 的末尾,就不再有元素产出,所以什么元素都没有输出。