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 from
Python 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__()
戻り値が正しく伝播されることを保証します。Generator
gen = Generator(Generator(test()))