Contents
30.4.6. 生成器表达式代替列表推导¶
列表推导可以根据输入序列中的每个元素创建一个包含派生元素的新列表,如果输入的数据量比较小,那么这样做没问题,但如果数据量很大,那么程序就有可能因为内存耗尽而崩溃。
value = [len(x) for x in open("my_numbers.txt")]
print(value)
"""
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2]
"""
要想处理大规模的数据,可以使用生成器表达式(generator expression)来做,它扩展了列表推导式与生成器机制。
程序在对生成器表达式求值时,并不会让它把包含输出结果的那个序列立刻构建出来,而是会把它当成一个迭代器,该迭代器每次可以根据表达式中的逻辑给出一项结果。
生成器表达式的写法,与列表推导式语法类似,但它是写在一对圆括号内,而不是方括号里面。
下面这种写法的效果与刚才一样,但是程序并不会立刻给出全部结果,而是先将生成器表达式表示成一个迭代器返回。
it = (len(x) for x in open("my_numbers.txt"))
print(it)
print(next(it))
print(next(it))
"""
<generator object <genexpr> at 0x0000024B4D2514F8>
3
3
"""
生成器表达式还有个强大的特性,就是可以组合起来。例如,可以用刚才那条生成器表达式所形成的it迭代器作为输入,编写一条新的生成器表达式。
这条表达式所形成的roots迭代器每次推进时,会引发连锁反应:它也推进内部迭代器it以判断当前是否还能在it上面继续迭代,如果可以,就把it所返回的值代入(x, x**0.5)里面求出结果。这种写法使用的内存同样不会太多
it = (len(x) for x in open("my_numbers.txt"))
print(it)
roots = ((x,x**0.5) for x in it)
print(next(roots))
"""
<generator object <genexpr> at 0x0000024B4D2C5E58>
(3, 1.7320508075688772)
"""
多个生成器嵌套而成的代码,执行起来还是相当快的。所以,如果要对数据量很大的输入流做一系列处理,那么生成器表达式应该是个很好的选择。唯一需要注意的是,生成器表达式返回的迭代器是有状态的,跑完一整轮之后,就不能继续使用了。
要点:
通过列表推导来处理大量的输入数据,可能会占用许多内存。改用生成器表达式来做,可以避免内存使用量过大的问题,因为这种表达式所形成的迭代器每次只会计算一项结果。
生成器表达式所形成的迭代器可以当成for语句的子表达式出现在另一个生成器表达式里。把生成器表达式组合起来使用,能够写出执行速度快且占用内存少的代码。