簡體   English   中英

Python 生成器與列表作為數組初始值設定項

[英]Python generator vs list as array initializer

這是一個使用列表 ( a ) 和使用元組生成器 ( b ) 初始化一千萬個隨機數數組的示例。 結果完全相同,從未使用過列表或元組,因此其中一個沒有實際優勢

from random import randint
from array import array

a = array('H', [randint(1, 100) for _ in range(0, 10000000)])
b = array('H', (randint(1, 100) for _ in range(0, 10000000)))

所以問題是使用哪一個。 原則上,我的理解是元組應該能夠使用比列表更少的資源來逃脫,但是由於沒有保留此列表和元組,因此代碼應該可以在不初始化中間數據結構的情況下執行……我的測試表明,在這種情況下,列表會稍微快一些。 我只能想象這是因為 Python 實現對列表的優化比元組更多。 我可以期望這是一致的嗎?

更一般地說,我應該使用一種還是另一種,為什么? (或者我應該完全以其他方式進行這種初始化。)

更新:答案和評論讓我意識到b示例實際上不是一個元組,而是一個generator ,所以我在標題和上面的文本中編輯了一些以反映這一點。 我還嘗試將列表版本拆分為兩行,這樣應該會強制實際實例化列表:

g = [randint(1, 100) for _ in range(0, 10000000)]
a = array('H', g)

它似乎沒有什么區別。 列表版本大約需要 8.5 秒,生成器版本大約需要 9 秒。

雖然看起來像, (randint(1, 100) for _ in range(0, 1000000))不是元組,它是一個生成器:

>>> type((randint(1, 100) for _ in range(0, 1000000)))
<class 'generator'>
>>>

如果你真的想要一個元組,請使用:

b = array('H', tuple(randint(1, 100) for _ in range(0, 1000000)))

列表比生成器快一點是有道理的,因為生成器在被要求時生成下一個值,一次一個,而列表推導式分配所有所需的內存,然后一口氣用值填充它。 這種速度優化是在內存空間中支付的。

我更喜歡生成器,因為無論最合理的內存限制如何,它都可以工作,並且可以用於任意數量的隨機數,而列表的加速是最小的。 除非您需要一次又一次地生成此列表,此時加速將開始計算 - 但是您可能每次開始時都使用列表的相同副本。

[randint(1, 100) for _ in range(0, 10000000)]

這是一個列表理解。 每個元素都在一個緊密的循環中進行評估並放在一個列表中,因此它通常更快但需要更多的 RAM(所有內容都立即出現)。

(randint(1, 100) for _ in range(0, 10000000))

這是一個生成器表達式。 此時不評估任何元素,當您在生成的生成器上調用next()時,其中一個會出現。 它較慢,但需要一致的(小)內存量。

正如另一個答案中給出的,如果你想要一個元組,你應該將其轉換為一個:

tuple([randint(1, 100) for _ in range(0, 10000000)])
tuple(randint(1, 100) for _ in range(0, 10000000))

讓我們回到你的問題:

什么時候用哪個?

通常,如果您使用列表推導式或生成器表達式作為另一個順序數據結構( listarray等)的初始化程序,除了上面提到的內存時間權衡之外,沒有任何區別。 您需要考慮的事情就像性能和內存預算一樣簡單。 如果您需要更高的速度(或編寫一個絕對快的 C 程序)或生成器表達式,如果您需要保持低內存消耗,您更喜歡列表理解。

如果您打算重用生成的序列,事情就會開始變得有趣。

列表嚴格來說是一個列表,並且可以用於所有目的作為列表:

a = [i for i in range(5)]
a[3]  # 3
a.append(5)            # a = [0, 1, 2, 3, 4, 5]
for _ in a:
    print("Hello")
                       # Prints 6 lines in total
for _ in a:
    print("Bye")
                       # Prints another 6 lines
b = list(reversed(a))  # b = [5, 4, 3, 2, 1, 0]

一個生成器只能使用一次。

a = (i for i in range(5))
a[3]                   # TypeError: generator object isn't subscriptable
a.append(5)            # AttributeError: generator has no attribute 'append'
for _ in a:
    print("Hello")
                       # Prints 5 lines in total
for _ in a:
    print("Bye")
                       # Nothing this time, because
                       # the generator has already been consumed
b = list(reversed(a))  # TypeError: generator isn't reversible

最后的答案是:知道你想做什么,並為它找到合適的數據結構。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM