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

Why converting list to set is faster than converting generator to set?

这是一个例子

>>> 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

非常混乱。生成器创建所需的时间更少(这是可以理解的)但是为什么将生成器转换为集合比转换列表慢(至少据我所知)恰恰相反。

首先,为生成器表达式的创建计时没有意义。创建生成器不会遍历内容,因此速度非常快。找出在一个元素上创建生成器表达式与在超过 1000 万个元素上创建生成器表达式之间的区别:

>>> 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?。涉及此过程的管理需要时间。相比之下,列表理解不必花费这个时间,它会循环执行所有操作,而不会为每个产生的值重新激活和停用函数。

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