89

これは、 Python ジェネレーター関数を何に使用できますか?の逆です。: python ジェネレーター、ジェネレーター式、およびitertoolsモジュールは、最近の python の私のお気に入りの機能の一部です。これらは、大量のデータに対して一連の操作を実行するように設定する場合に特に便利です。DSV ファイルを処理するときによく使用します。

では、ジェネレーター、ジェネレーター式、または関数を使用するのに適していないitertoolsのはどのような場合でしょうか?

  • zip()itertools.izip()または_
  • range()以上xrange()、または
  • [x for x in foo]以上(x for x in foo)

明らかに、最終的にはジェネレーターを実際のデータに「解決」する必要があります。通常は、リストを作成するか、ジェネレーター以外のループで反復処理します。長さを知る必要がある場合もあります。これは私が求めているものではありません。

中間データ用に新しいリストをメモリに割り当てないように、ジェネレータを使用します。これは、大規模なデータセットの場合に特に意味があります。小さなデータセットにも意味がありますか? 顕著なメモリ/CPU のトレードオフはありますか?

リスト内包表記のパフォーマンスと map() および filter()の目を見張るような議論に照らして、誰かがこれについてプロファイリングを行っているかどうかに特に興味があります。(代替リンク)

4

10 に答える 10

62

次の場合は、ジェネレーターの代わりにリストを使用します。

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) 文字列を結合する必要があります (データに対して 2 つのパスが必要です)。

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

4) PyPyを使用しているため、通常の関数呼び出しとリスト操作でジェネレーター コードを最大限に最適化できないことがあります。

于 2014-10-29T16:36:57.670 に答える
42

一般に、len()、reversed() などのリスト操作が必要な場合は、ジェネレーターを使用しないでください。

遅延評価が必要ない場合もあります (たとえば、リソースを解放できるようにすべての計算を前もって実行する場合など)。その場合、リスト式の方がよいかもしれません。

于 2008-10-29T04:42:05.870 に答える
26

プロファイル、プロファイル、プロファイル。

コードのプロファイリングは、実行していることが何らかの効果があるかどうかを知る唯一の方法です。

xrange、ジェネレーターなどのほとんどの使用法は、静的サイズ、小さなデータセットを超えています。それが本当に違いを生むのは、大きなデータセットに到達したときだけです。range()とxrange()は、ほとんどの場合、コードを少し醜く見せ、何も失わず、何かを得るだけの問題です。

プロファイル、プロファイル、プロファイル。

于 2008-10-29T11:37:31.913 に答える
17

ジェネレータ内包表記よりも、 、リスト内包表記、またはリスト内包表記を優先zipするべきではありません。Python 3.0では、-like セマンティクスとhas -like セマンティクスがあります。iziprangexrangerangexrangezipizip

list(frob(x) for x in foo)リスト内包表記は、実際のリストが必要な場合のように、実際にはより明確です。

于 2008-10-29T04:28:58.643 に答える
7

「これは特に大規模なデータセットの場合に理にかなっています」と述べたように、これはあなたの質問に答えると思います。

パフォーマンスに関して壁にぶつかっていない場合でも、リストと標準関数に固執することができます。その後、パフォーマンスに問題が発生した場合は、切り替えてください。

ただし、@ u0b34a0f6ae がコメントで述べたように、最初にジェネレーターを使用すると、より大きなデータセットへのスケーリングが容易になります。

于 2008-10-29T08:50:47.117 に答える
6

パフォーマンスに関して: 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
于 2008-11-01T05:53:31.667 に答える
3

ジェネレーターがあなたがやろうとしていることを妨げるような状況を私は見つけたことがありません。ただし、ジェネレーターを使用しても、ジェネレーターを使用しないよりも役に立たない場合がたくさんあります。

例えば:

sorted(xrange(5))

以下を超える改善はありません。

sorted(range(5))
于 2008-10-29T16:44:36.833 に答える
3

後で何かのために値を保持する必要があり、セットのサイズが大きすぎない場合は、リスト内包表記を優先する必要があります。

例: プログラムで後で数回ループするリストを作成しています。

ジェネレーターは反復 (ループ) の代わりと考えることができ、リスト内包表記はデータ構造の初期化の一種と考えることができます。データ構造を維持したい場合は、リスト内包表記を使用してください。

于 2008-11-01T23:49:09.560 に答える
2

パフォーマンスに関しては、ジェネレーターでリストを使用したいと思うことはありません。

于 2008-10-29T11:44:06.313 に答える