Pythonジェネレータから「戻り値」を受け取る最良の方法 質問する

Pythonジェネレータから「戻り値」を受け取る最良の方法 質問する

Python 3.3 以降では、ジェネレータ関数が値を返すと、それが StopIteration 例外の値として発生します。これはいくつかの方法で収集できます。

  • 式の値yield from。これは、囲んでいる関数もジェネレーターであることを意味します。
  • 呼び出しを try/except ブロックでラップしnext()ます.send()

ただし、最も簡単な方法である for ループでジェネレーターを反復処理するだけの場合、StopIteration 例外の値、つまり戻り値を収集する方法はないようです。ジェネレーターが値を生成し、最後に何らかの要約 (実行合計、平均、タイミング統計など) を返す簡単な例を使用しています。

for i in produce_values():
    do_something(i)

values_summary = ....??

1 つの方法は、ループを自分で処理することです。

values_iter = produce_values()
try:
    while True:
        i = next(values_iter)
        do_something(i)
except StopIteration as e:
    values_summary = e.value

しかし、これでは for ループのシンプルさが失われます。呼び出しコード自体がジェネレーターである必要があるため、使用できませんyield from。上記のロール ワンズ オウン for ループよりも簡単な方法はありますか?

ベストアンサー1

valueの属性StopIteration(およびおそらくStopIteration自体) は、「通常の」コードで使用するように設計されていない実装の詳細と考えることができます。

見てペップ380yield fromPython 3.3 の機能を指定します。StopIteration戻り値を返すために を使用するいくつかの代替手段が検討されていることについて説明します。

通常のループでは戻り値を取得することは想定されていないためfor、そのための構文はありません。StopIteration明示的にキャッチすることが想定されていないのと同じです。

あなたの状況に対する良い解決策は、小さなユーティリティ クラス (標準ライブラリには十分役立つかもしれません) です。

class Generator:
    def __init__(self, gen):
        self.gen = gen

    def __iter__(self):
        self.value = yield from self.gen
        return self.value

これは任意のジェネレータをラップし、その戻り値をキャッチして後で検査します。

>>> def test():
...     yield 1
...     return 2
...
>>> gen = Generator(test())
>>> for i in gen:
...    print(i)
...
1
>>> print(gen.value)
2

return self.valueの行は、インスタンスがネストされている場合でも (例)、__iter__()戻り値が正しく伝播されることを保証します。Generatorgen = Generator(Generator(test()))

おすすめ記事