1

ジェネレーターの次の 2 つの定義のどちらかを決定しようとしています。どちらが良いですか?「よりpythonic」なのはどれですか?そして、それぞれの欠点を軽減する方法はありますか?

def myGenerator1(howMany):
    result = [0,0,0]
    yield result
    for i in range(howMany)
        modifyListInPlace(result)
        yield result

for val in myGenerator1(1000):
    useValThenForgetIt(val)

def myGenerator2(howMany):
    result = (0,0,0)
    yield result
    for i in range(howMany)
        result = createNewUpdatedTuple(result)
        yield result

for val in myGenerator2(1000):
    useValThenForgetIt(val)

最初のものは、ジェネレーターによって返された値を変更します。おそらく、私がまだ予見していなかった呼び出しコードを台無しにします。2 番目は、この場合、1000 タプル相当のガベージを生成します。「howMany」を増やすと (可能性があります)、それ以上になります。

例として挙げたループは、ジェネレーターの現在の使用にすぎません。そこから得られる値を保存することはないと思いますが、他の場所で役立つ可能性のあるちょっとしたユーティリティです。

4

2 に答える 2

3

ガイドとして標準ライブラリを見ると、基になるアルゴリズムが mutate-in-place アルゴリズムであるにもかかわらず、 itertoolsモジュールの組み合わせ関数はすべてタプルを返します。たとえば、itertools.permutationsのコードを見てください。

この設計 (リストの代わりにタプルを返す) は堅牢であることが証明されています。呼び出し元がイテレータの戻り値で何をしているかによって、変更リスト アプローチが見つけにくいバグを作成するのではないかと心配しています。

もう1つの考え。未使用の結果について「何千ものタプルに相当するガベージを作成する」ことについてあまり心配する必要はありません。Python のタプル実装は、以前に破棄されたタプルを再利用するのに非常に優れています (フリーリストの配列を使用することにより、メモリ アロケータを呼び出すことなく、以前に使用されたタプルから新しいタプルを作成できます)。そのため、タプル バージョンは、リスト バージョンと同じか、それより少し優れたパフォーマンスを発揮します。

于 2011-11-16T06:07:31.030 に答える
1

最初のオブジェクトがオブジェクトを返すことができ、それが返された後に明らかにそれを変更できるという事実は、使用している言語に関係なく、私には大きなコードの臭いです(つまり、「pythonic」であるという問題ではありません)。さらに、同じ値のイテレータを何度も繰り返し生成し、yield間で変更する関数が必要なのはなぜですか?私には非常に直感的ではないようです。

値を使用する場合、によって作成されたタプルmyGenerator2はガベージではありません。それらを一度に1つずつ使用する場合、それらがすべて同時に存在することは決してなく、プログラムはほぼ確実に他の多くのメモリ割り当て/割り当て解除を実行します。によって返されるリストとは異なり、range(howMany)実際には使用しない1,000個の整数作成されます(Python3を使用している場合を除きます。この場合range、リストではなくジェネレーターが返されます)。

呼び出し元がジェネレーターから返されたものへの参照を保持したい場合があります(Pythonプログラマーは通常、ジェネレーターが与えられたときに、それらを複数回使用する必要がある場合にアクセスできるitems = list(generator)ことを期待しています) 、次に2番目がはるかに優れています。

于 2011-11-16T05:53:24.270 に答える