web-dev-qa-db-ja.com

ジェネレーターのraise StopIterationとreturnステートメントの違いは何ですか?

ジェネレーターでのraise StopIterationreturnステートメントの違いについて知りたいです。

たとえば、これら2つの機能に違いはありますか?

def my_generator0(n):
    for i in range(n):
        yield i
        if i >= 5:
            return

def my_generator1(n):
    for i in range(n):
        yield i
        if i >= 5:
            raise StopIteration

それを行うためのより「Python的な」方法は2番目の方法だと思います(私が間違っている場合は修正してください)が、両方の方法でStopIteration例外が発生することがわかる限り。

38
slallum

StopIterationを明示的に発生させる必要はありません。これは、ジェネレータ関数に対してreturnステートメントを実行するだけなので、同じです。しかし、いいえ、returnを使用するだけの方がPythonicです。

From: http://docs.python.org/2/reference/simple_stmts.html#the-return-statement (Python 3.2に有効)

ジェネレーター関数では、returnステートメントにexpression_listを含めることはできません。そのコンテキストでは、ベアリターンはジェネレータが完了し、StopIterationが発生することを示します。

または、@ Bakuriuが指摘しているように、ジェネレータのセマンティクスはPython 3.3でわずかに変更されているため、以下の方が適切です。

ジェネレーター関数では、returnステートメントはジェネレーターが完了し、StopIterationが発生することを示します。戻り値(存在する場合)は、StopIterationを構築するための引数として使用され、StopIteration.value属性になります。

46
Jon Clements

2014年後半の時点で、returnは正しく、発電機を終了するためのraise StopIterationは減価償却スケジュールに従っています。詳細については PEP 479 を参照してください。

概要

このPEPは、ジェネレーターへの変更を提案しています。StopIterationがジェネレーター内で発生すると、RuntimeErrorに置き換えられます。 (より正確には、これは、例外がジェネレーターのスタックフレームからバブリングしようとしているときに発生します。)変更は下位互換性がないため、最初に__future__ステートメントを使用して機能が導入されます。

受け入れ

このPEPは11月22日にBDFLによって承認されました…

根拠

ジェネレーターとStopIterationの相互作用は現在、いくぶん驚くべきものであり、あいまいなバグを隠すことができます。予期しない例外によって、動作が微妙に変更されることはありませんが、ノイズが多く、簡単にデバッグできるトレースバックが発生するはずです。現在、ジェネレーター関数内で偶発的に発生したStopIterationは、ジェネレーターを駆動するループ構造によって反復の終了として解釈されます。

18
Blake Walsh

それは真実です。一方は読み取り可能で、もう一方は不明瞭である点を除いて、それらは同等です。これは、最初のバージョンのジェネレーター(PEP 255、 "Specification:Return"の下)にさかのぼり、その後の(コルーチンなどの)拡張によってこれが変更されることはありません。 3.3の_yield from_(PEP 380)は、raise StopIteration(<expr>)の構文糖として_return <expr>_に拡張しますが、_return;_の意味は変わりません。

5
user395760