生成器表达式 vs 列表推导式

522

在Python中,何时应使用生成器表达式,何时应使用列表推导?

# Generator expression
(x*2 for x in range(256))

# List comprehension
[x*2 for x in range(256)]

41
[exp for x in iter] 只是 list((exp for x in iter)) 的语法糖吗?还是有执行上的差别? - b0fh
1
我认为我有一个相关的问题,当使用yield时,我们可以仅从函数中使用生成器表达式,还是必须使用yield使函数返回生成器对象? - user1176501
40
回复@b0fh的评论有些晚了:在Python2中,列表解析时循环变量会泄漏出来,而生成器表达式则不会泄漏。比较X = [x**2 for x in range(5)]; print xY = list(y**2 for y in range(5)); print y,后者会报错。但在Python3中,列表解析确实是你所期望的被馈送到list()函数中的生成器表达式的语法糖,因此循环变量将不会再泄漏出来。详见PEP 0289 - Bas Swinckels
21
我建议阅读PEP 0289,简而言之就是“引入生成器表达式作为列表推导和生成器的高性能、内存高效的泛化”。此外,它还有使用它们的有用示例。 - icc97
5
我也晚了八年才参加派对,PEP链接非常完美。谢谢你让我轻松找到它! - eenblam
显示剩余2条评论
13个回答

3
列表推导式是急切的,但生成器是懒惰的。
在列表推导式中,所有对象都会立即创建,这需要更长的时间来创建和返回列表。在生成器表达式中,对象的创建被延迟到通过next()请求时才进行。在next()时,生成器对象会立即创建并返回。
由于对象已经创建,因此列表推导式中的迭代速度更快。
如果您遍历列表推导式和生成器表达式中的所有元素,则时间性能大约相同。即使生成器表达式立即返回生成器对象,它也不会创建所有元素。每次迭代新元素时,它将创建并返回它。
但是,如果您没有遍历所有元素,则生成器更有效率。假设您需要创建包含数百万个项目的列表推导式,但只使用其中的10个。您仍然必须创建数百万个项目。您只是在为创建数百万个项目而浪费时间。或者,如果您正在进行数百万个API请求,但最终只使用其中的10个。由于生成器表达式是懒惰的,除非有请求,否则它不会进行所有计算或API调用。在这种情况下,使用生成器表达式会更有效率。
在列表推导式中,整个集合都加载到内存中。但是,生成器表达式一旦在next()调用时向您返回一个值,它就完成了,并且不需要再将其存储在内存中。只有一个单独的项目加载到内存中。如果您正在遍历磁盘上的大型文件,如果文件太大,则可能会出现内存问题。在这种情况下,使用生成器表达式会更有效率。

1

我认为大多数答案都忽略了一些东西。列表生成式基本上创建一个列表并将其添加到堆栈中。在列表对象非常大的情况下,您的脚本进程会被终止。在这种情况下,最好使用生成器,因为其值不会存储在内存中,而是作为有状态的函数存储。此外,创作速度;列表推导比生成器推导更慢。

简而言之:如果对象的大小没有过大,则使用列表推导,否则请使用生成器推导。


0

在函数式编程中,我们希望尽可能少地使用索引。因此,如果我们想要在获取第一批元素后继续使用这些元素,islice() 是更好的选择,因为迭代器状态会被保存。

from itertools import islice

def slice_and_continue(sequence):
    ret = []
    seq_i = iter(sequence) #create an iterator from the list

    seq_slice = islice(seq_i,3) #take first 3 elements and print
    for x in seq_slice: print(x),

    for x in seq_i: print(x**2), #square the rest of the numbers

slice_and_continue([1,2,3,4,5])

输出:1 2 3 16 25


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接