为什么将列表转换为set比将生成器转换为set更快?

时间:2017-09-06 16:30:58

标签: python performance python-3.x list-comprehension generator-expression

这是一个例子

>>> from timeit import timeit
>>> print(timeit('[y for y in range(100)]', number=100000))
0.7025867114395824
>>> print(timeit('(y for y in range(100))', number=100000))
0.09295392291478244
>>> print(timeit('set([y for y in range(100)])', number=100000))
1.0864544935180334
>>> print(timeit('set((y for y in range(100)))', number=100000))
1.1277489876506621

这非常令人困惑。生成器花费的时间更少(这是可以理解的)但是为什么将生成器转换为set比转换列表要慢(至少据我所知)是相反的。

1 个答案:

答案 0 :(得分:3)

首先,创建生成器表达式的时机没有意义。创建生成器不会迭代内容,所以它非常快。发现在一个元素上创建生成器表达式与超过一千万个元素之间的差异:

>>> print(timeit('(y for y in range(1))', number=100000))
0.060932624037377536
>>> print(timeit('(y for y in range(10000000))', number=100000))
0.06168231705669314

生成器需要更多时间来迭代,比如列表对象:

>>> from collections import deque
>>> def drain_iterable(it, _deque=deque):
...     deque(it, maxlen=0)
...
>>> def produce_generator():
...     return (y for y in range(100))
...
>>> print(timeit('drain_iterable(next(generators))',
...              'from __main__ import drain_iterable, produce_generator;'
...              'generators=iter([produce_generator() for _ in range(100000)])',
...              number=100000))
0.5204695729771629
>>> print(timeit('[y for y in range(100)]', number=100000))
0.3088444779859856

这里我只用discarding all elements as fast as possible测试了生成器表达式的迭代。

这是因为生成器本质上是一个正在执行的函数,直到它产生一个值,然后暂停,然后再次激活下一个值,然后再次暂停。有关概述,请参阅What does the "yield" keyword do?。涉及此过程的管理需要时间。相比之下,列表理解不必花费这个时间,它会在没有为每个产生的值重新激活和取消激活函数的情况下进行所有循环。

生成器内存高效,而不是执行效率高。它们可以节省执行时间,有时,但通常是因为您避免分配和释放更大的内存块。