2

下面是一個例子爲什麼將列表轉換爲集合比將生成器轉換爲集合更快?

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

這是非常混亂。生成器需要更少的時間來創建(這是可以理解的),但是爲什麼將生成器轉換爲集合要比轉換列表要慢(至少據我所知)是相反的。

+2

'(y for range in(100))'不會做任何事情,只能創建一個生成器對象。沒有迭代完成,所以與其他發生的事情相比,這是一個毫無價值的測試。 –

+1

發生器的優點是在內存消耗和提前終止的可能性;迭代整個生成器不會比迭代整個相應的列表理解更快。 – user2357112

回答

3

首先,沒有時間創建生成器表達式。創建一個生成器不會遍歷內容,所以它非常快。現貨與超過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?以獲得良好的概述。涉及這一過程的管理部門需要時間。相比之下,列表理解不需要花費這段時間,它可以循環所有循環,而無需重新激活和取消激活每個產生的值的函數。

發生器是高效內存,執行效率不高。他們可以節省執行時間,有時候是,但通常是因爲您避免分配和釋放更大的內存塊。

+0

好吧,你的意思是,因爲發電機需要更多的時間來進行迭代而不是列表;轉換髮生器設置比轉換列表慢。感謝您的澄清。我不知道(或者我認爲是錯誤的)比生成器慢於列表。 –

+0

@IsaacVassell:exactly;所有'set()'都會遍歷輸入以添加結果元素,所以我專注於它們的速度差異。 –