yield
の仕組みを知っています。私は順列を知っています、それは数学の単純さとしてそれを考えてください。
しかし、yield
の真の力とは何でしょうか。いつ使用すべきですか?シンプルで良い例が良いです。
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
しかし、私は多くのメモリ使用量を節約します。
簡単に言えば、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
を使用して、ある種のスタック変数を使用する代わりに、コールスタックを介して物事をプッシュします。ジェネレーターは、特殊なツリートラバーサルやその他のあらゆる方法にも使用できます。
参考文献:
もう1つの用途はネットワーククライアントです。ジェネレーター関数で 'yield'を使用して、スレッドを複雑にすることなく複数のソケットをラウンドロビンします。
たとえば、イメージのR、G、Bプレーンをファームウェアに送信する必要があるハードウェアテストクライアントがありました。データはロックステップで送信する必要がありました:赤、緑、青、赤、緑、青。 3つのスレッドを生成するのではなく、ファイルから読み取り、バッファーをエンコードするジェネレーターを用意しました。各バッファは「yield buf」でした。ファイルの終わり、関数が返され、反復の終わりがありました。
私のクライアントコードは3つのジェネレーター関数をループし、反復の終わりまでバッファーを取得しました。
私は読んでいます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
したがって、おそらく、次の要素がデジタルフィルターなどの前の要素に依存している場合は、利回りを使用するときが来たと思います。