4

これら 2 つのコード フラグメントは、リストの作成方法のみが異なります。1 つは を使用し[]、もう1 つは を使用しlist()ます。

これは iterable を消費し、次に a を発生させStopIterationます:

>>> try:
...     iterable = iter(range(4))
...     while True:
...         print([next(iterable) for _ in range(2)])
... except StopIteration:
...     pass
...
[0, 1]
[2, 3]

これはイテラブルを消費し、空のリストを出力して永久にループします。

>>> try:
...     iterable = iter(range(4))
...     while True:
...         print(list(next(iterable) for _ in range(2)))
... except StopIteration:
...     pass
...
[0, 1]
[2, 3]
[]
[]
[]
etc.

この行動のルールは何ですか?

4

1 に答える 1

3

PEP479を参照してください。

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

(私のものを強調)

そのため、 のコンストラクターは、エラーが発生するまで ( 2 番目の引数なしで呼び出して) list、渡されたジェネレーター式を反復処理します。もう一つの例:StopIterationnext(iterable)

def f():
    raise StopIteration # explicitly

def g():
    return 'g'

print(list(x() for x in (g, f, g))) # ['g']
print([x() for x in (g, f, g)]) # `f` raises StopIteration

一方、 * 内包表記はStopIteration、呼び出し元に伝達されるため、動作が異なります。


リンクされた PEP が提案した動作は次のとおりです。

aStopIterationがジェネレーター フレームからバブルアウトしようとしている場合、それは に置き換えられますRuntimeError。これにより、next()(ジェネレーターを呼び出した) 呼び出しが失敗し、その例外が渡されます。それ以降は、古い例外と同じです。

Python 3.5 は、次を使用して有効にできるgenerator_stop機能を追加しました

from __future__ import generator_stop

この動作は、Python 3.7 ではデフォルトになります。

于 2015-07-11T19:56:07.663 に答える