26

今日、値を抽出するために使用していたために発生したバグをnext()見つけましたが、「見つかりません」はStopIteration.

通常、それはプログラムを停止しますが、使用している関数は反復next内で呼び出されていたため、は早期に終了して返されました。all()allTrue

これは予期される動作ですか? この種のことを避けるのに役立つスタイルガイドはありますか?

簡単な例:

def error(): return next(i for i in range(3) if i==10)
error() # fails with StopIteration
all(error() for i in range(2)) # returns True
4

2 に答える 2

24

これは Python バージョン 3.6 までのデフォルトの動作ですが、これは言語の誤りと見なされ、代わりに例外が発生するように Python 3.7 で変更される予定です。

PEP 479が言うように:

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

Python 3.5 以降では、デフォルトの動作を 3.7 で予定されている動作に変更できます。このコード:

# gs_exc.py

from __future__ import generator_stop

def error():
    return next(i for i in range(3) if i==10)

all(error() for i in range(2))

… 次の例外が発生します。

Traceback (most recent call last):
  File "gs_exc.py", line 8, in <genexpr>
    all(error() for i in range(2))
  File "gs_exc.py", line 6, in error
    return next(i for i in range(3) if i==10)
StopIteration

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "gs_exc.py", line 8, in <module>
    all(error() for i in range(2))
RuntimeError: generator raised StopIteration

インポートのないPython 3.5 および 3.6__future__では、警告が発生します。例えば:

# gs_warn.py

def error():
    return next(i for i in range(3) if i==10)

all(error() for i in range(2))

$ python3.5 -Wd gs_warn.py 
gs_warn.py:6: PendingDeprecationWarning: generator '<genexpr>' raised StopIteration
  all(error() for i in range(2))

$ python3.6 -Wd gs_warn.py 
gs_warn.py:6: DeprecationWarning: generator '<genexpr>' raised StopIteration
  all(error() for i in range(2))
于 2015-02-03T00:17:46.017 に答える
9

問題は を使用するallことではなく、 へのパラメーターとしてジェネレーター式があることですall。はStopIterationジェネレーター式に伝播されますが、ジェネレーター式はそれがどこで発生したかを実際には知らないため、通常のことを実行して反復を終了します。

errorこれは、関数をエラーを直接発生させるものに置き換えることで確認できます。

def error2(): raise StopIteration

>>> all(error2() for i in range(2))
True

パズルの最後のピースはall、空のシーケンスで何が行われるかを知ることです。

>>> all([])
True

next直接使用する場合は、StopIteration自分自身をキャッチする準備をする必要があります。

編集: Python 開発者がこれをバグと見なし、3.7 で変更するための措置を講じていることを確認できてうれしいです。

于 2015-02-02T23:52:14.943 に答える