簡體   English   中英

什么時候不適合使用 python 生成器?

[英]When is not a good time to use python generators?

這與您可以使用 Python 生成器函數做什么相反 :python 生成器、生成器表達式和itertools模塊是我itertools最喜歡的 Python 功能。 它們在設置操作鏈以對大量數據執行時特別有用——我在處理 DSV 文件時經常使用它們。

那么什么時候適合使用生成器、生成器表達式或itertools函數呢?

  • 我什么時候應該更喜歡zip()不是itertools.izip() ,或者
  • range()超過xrange() ,或
  • [x for x in foo] over (x for x in foo) ?

顯然,我們最終需要將生成器“解析”為實際數據,通常是通過創建一個列表或使用非生成器循環對其進行迭代。 有時我們只需要知道長度。 這不是我要問的。

我們使用生成器,這樣我們就不會為臨時數據分配新列表到內存中。 這對於大型數據集尤其有意義。 它對小數據集也有意義嗎? 是否有明顯的內存/CPU 權衡?

鑒於對列表理解性能與 map() 和 filter()的令人大開眼界的討論,如果有人對此進行了一些分析,我特別感興趣。 替代鏈接

在以下情況下使用列表而不是生成器:

1)您需要訪問數據多次(即高速緩存的結果,而不是重新計算它們的):

for i in outer:           # used once, okay to be a generator or return a list
    for j in inner:       # used multiple times, reusing a list is better
         ...

2)您需要隨機訪問(或除前向順序以外的任何訪問):

for i in reversed(data): ...     # generators aren't reversible

s[i], s[j] = s[j], s[i]          # generators aren't indexable

3)您需要加入字符串(這需要對數據進行兩次傳遞):

s = ''.join(data)                # lists are faster than generators in this use case

4) 您使用的PyPy有時無法像使用普通函數調用和列表操作那樣優化生成器代碼。

一般來說,當你需要列表操作時不要使用生成器,比如 len()、reversed() 等等。

有時您也可能不想進行惰性求值(例如,預先進行所有計算以便釋放資源)。 在這種情況下,列表表達式可能更好。

簡介,簡介,簡介。

分析您的代碼是了解您所做的事情是否有任何影響的唯一方法。

xrange、生成器等的大多數用途都超過靜態大小、小數據集。 只有當您處理大型數據集時,它才會真正發揮作用。 range() 與 xrange() 主要只是讓代碼看起來更難看一點,並且不會丟失任何東西,並且可能會獲得一些東西。

簡介,簡介,簡介。

你永遠不應該喜歡zip不是iziprange不是xrange ,或者列表izip不是生成器izip 在 Python 3.0 中, range具有izip xrange的語義,而zip具有izip的語義。

列表list(frob(x) for x in foo)實際上更清晰,例如list(frob(x) for x in foo)在您需要實際列表的時候。

正如您提到的,“這對於大型數據集尤其有意義”,我認為這回答了您的問題。

如果您沒有碰到任何障礙,在性能方面,您仍然可以堅持使用列表和標准功能。 然后,當您遇到性能問題時,請進行切換。

然而,正如@u0b34a0f6ae 在評論中提到的,在開始時使用生成器可以讓您更輕松地擴展到更大的數據集。

關於性能:如果使用 psyco,列表可能比生成器快很多。 在下面的示例中,使用 psyco.full() 時,列表速度快了近 50%

import psyco
import time
import cStringIO

def time_func(func):
    """The amount of time it requires func to run"""
    start = time.clock()
    func()
    return time.clock() - start

def fizzbuzz(num):
    """That algorithm we all know and love"""
    if not num % 3 and not num % 5:
        return "%d fizz buzz" % num
    elif not num % 3:
        return "%d fizz" % num
    elif not num % 5:
        return "%d buzz" % num
    return None

def with_list(num):
    """Try getting fizzbuzz with a list comprehension and range"""
    out = cStringIO.StringIO()
    for fibby in [fizzbuzz(x) for x in range(1, num) if fizzbuzz(x)]:
        print >> out, fibby
    return out.getvalue()

def with_genx(num):
    """Try getting fizzbuzz with generator expression and xrange"""
    out = cStringIO.StringIO()
    for fibby in (fizzbuzz(x) for x in xrange(1, num) if fizzbuzz(x)):
        print >> out, fibby
    return out.getvalue()

def main():
    """
    Test speed of generator expressions versus list comprehensions,
    with and without psyco.
    """

    #our variables
    nums = [10000, 100000]
    funcs = [with_list, with_genx]

    #  try without psyco 1st
    print "without psyco"
    for num in nums:
        print "  number:", num
        for func in funcs:
            print func.__name__, time_func(lambda : func(num)), "seconds"
        print

    #  now with psyco
    print "with psyco"
    psyco.full()
    for num in nums:
        print "  number:", num
        for func in funcs:
            print func.__name__, time_func(lambda : func(num)), "seconds"
        print

if __name__ == "__main__":
    main()

結果:

without psyco
  number: 10000
with_list 0.0519102208309 seconds
with_genx 0.0535933367509 seconds

  number: 100000
with_list 0.542204280744 seconds
with_genx 0.557837353115 seconds

with psyco
  number: 10000
with_list 0.0286369007033 seconds
with_genx 0.0513424889137 seconds

  number: 100000
with_list 0.335414877839 seconds
with_genx 0.580363490491 seconds

就性能而言,我想不出任何時候您會想要在生成器上使用列表。

我從未發現生成器會阻礙您嘗試做的事情的情況。 然而,在很多情況下,使用生成器與不使用它們一樣對您沒有幫助。

例如:

sorted(xrange(5))

不提供任何改進:

sorted(range(5))

如果您以后需要將值保留在其他地方並且您的集合的大小不太大,您應該更喜歡列表推導式。

例如:您正在創建一個列表,稍后您將在程序中多次循環該列表。

在某種程度上,您可以將生成器視為迭代(循環)與列表推導式的替代品,作為一種數據結構初始化。 如果要保留數據結構,請使用列表推導式。

生成器構建可枚舉的值列表。 當迭代過程可以按需使用值時,可枚舉非常有用。 構建你的生成器需要時間,所以如果列表大小為數百萬條記錄,使用sql server處理sql中的數據可能更有用。

暫無
暫無

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

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