web-dev-qa-db-ja.com

Python最適な場所で利回りを使用するには?

yieldの仕組みを知っています。私は順列を知っています、それは数学の単純さとしてそれを考えてください。

しかし、yieldの真の力とは何でしょうか。いつ使用すべきですか?シンプルで良い例が良いです。

32
whi

yieldは、シーケンスを返す関数があり、そのシーケンスを反復処理したいが、メモリ内のすべての値を一度に持つ必要がない場合に最適です。

たとえば、CSVファイルの大きなリストを解析するpythonスクリプトがあり、各行を別の関数で処理するために返したいと思っています。メガバイトを格納したくないメモリ内のデータをすべて一度に収集するため、pythonデータ構造の各行をyieldします。したがって、ファイルから行を取得する関数は次のようになります。

def get_lines(files):
    for f in files:
        for line in f:
            #preprocess line
            yield line

次に、リストと同じ構文を使用して、この関数の出力にアクセスできます。

for line in get_lines(files):
    #process line

しかし、私は多くのメモリ使用量を節約します。

60
murgatroid99

簡単に言えば、yieldはジェネレーターを提供します。関数でreturnを通常使用する場所で使用します。プロンプトから切り取って貼り付けた本当に工夫された例として...

>>> def get_odd_numbers(i):
...     return range(1, i, 2)
... 
>>> def yield_odd_numbers(i):
...     for x in range(1, i, 2):
...             yield x
... 
>>> foo = get_odd_numbers(10)
>>> bar = yield_odd_numbers(10)
>>> foo
[1, 3, 5, 7, 9]
>>> bar
<generator object yield_odd_numbers at 0x1029c6f50>
>>> bar.next()
1
>>> bar.next()
3
>>> bar.next()
5

ご覧のとおり、最初のケースでは、fooはリスト全体を一度にメモリに保持します。 5つの要素を持つリストでは大した問題ではありませんが、500万のリストが必要な場合はどうでしょうか。これは巨大なメモリイーターであるだけでなく、関数が呼び出されたときにビルドするのに多くの時間がかかります。 2番目のケースでは、barはジェネレーターを提供するだけです。ジェネレータは反復可能です。つまり、forループなどで使用できますが、各値にアクセスできるのは1回だけです。すべての値が同時にメモリに保存されるわけではありません。ジェネレーターオブジェクトは、最後に呼び出したときにループしていた場所を「記憶」します。つまり、反復可能オブジェクトを使用して(たとえば)500億にカウントする場合、すべてを500億にカウントする必要はありません。一度に500億の数値を保存して、カウントします。繰り返しますが、これはかなり不自然な例です。本当に500億まで数えたい場合は、おそらくitertoolsを使用します。 :)

これは、ジェネレーターの最も単純な使用例です。あなたが言ったように、それは効率的な順列を書くために使用することができ、yieldを使用して、ある種のスタック変数を使用する代わりに、コールスタックを介して物事をプッシュします。ジェネレーターは、特殊なツリートラバーサルやその他のあらゆる方法にも使用できます。

参考文献:

15
waffle paradox

もう1つの用途はネットワーククライアントです。ジェネレーター関数で 'yield'を使用して、スレッドを複雑にすることなく複数のソケットをラウンドロビンします。

たとえば、イメージのR、G、Bプレーンをファームウェアに送信する必要があるハードウェアテストクライアントがありました。データはロックステップで送信する必要がありました:赤、緑、青、赤、緑、青。 3つのスレッドを生成するのではなく、ファイルから読み取り、バッファーをエンコードするジェネレーターを用意しました。各バッファは「yield buf」でした。ファイルの終わり、関数が返され、反復の終わりがありました。

私のクライアントコードは3つのジェネレーター関数をループし、反復の終わりまでバッファーを取得しました。

3
David Poole

私は読んでいますPythonのデータ構造とアルゴリズム

収量を使用したフィボナッチ関数があります。利回りを使うのに最適な時期だと思います。

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a+b

あなたはこれを次のように使うことができます:

gen = fibonacci()
for i, f in enumerate(gen):
    print(i, f)
    if i >= 100: break

したがって、おそらく、次の要素がデジタルフィルターなどの前の要素に依存している場合は、利回りを使用するときが来たと思います。

1
Xin Zhou