web-dev-qa-db-ja.com

pythonで反復可能オブジェクトを空にしないでください。それらを反復しようとすると例外が発生します

この動作はPython=非常に独特であり、特にリストで関数とメソッドを使用して、いくつかの操作を実行した後に別のリストを返す場合、多くのバグを引き起こす可能性があると思います。リスト内の要素。

x = []

val = (row for row in x) # create a generator
print(next(val)) # will raise StopIteration exception

for i in x:
    if not i:
        raise ValueError('The sequence is empty') # Exception is not raised
    print(i) # does nothing and does not raise an exception

nextを使用してジェネレーターを反復しようとして、それが空であるか最後に達した場合、StopIteration例外が発生しますが、forループを使用して反復する場合はこれは同じではありませんリストまたはPythonの反復可能オブジェクト。

if not x:リストが空かどうかを確認しますが、ループする利点がないため、リストを反復しようとすると、空の反復可能オブジェクトで例外が発生するはずです。

言語設計でこの動作に特別な理由があるかどうかを知りたいのですが、それが役立つかもしれない別のケースについては考えていません。 Javaについても同様だと思います。

3
danidee

ここで欠落していると思うのは、StopIterationが実際にforループを停止するものですこれが、Pythonのforループで透過的に動作するカスタムイテレータを記述できる理由です。イテレータが空であるかどうかの確認は行われず、forループはイテレータの状態を追跡しません。これはイテレータプロトコルの影響であり、言語設計で意図的に選択されました。必要に応じて、イテレータプロトコルの詳細を読むことができます Pythonのドキュメントサイト

たとえば、アイテムの数について考えると、より理にかなっています。ループが実行された回数に直接対応するリスト。

-----------------------------------|
| No. of items | No. times executed|
------------------------------------
|      5       |         5         |
|      4       |         4         |
|      3       |         3         |
|      2       |         2         |
|      1       |         1         |
|      0       |         0         |
------------------------------------

Forループが空のイテラブルを特殊なケースにした場合、この不変条件は失われます。

また、反復が始まる前に反復可能オブジェクトが空であることをforループに通知するためにextraメソッドを使用する必要があるため、カスタムイテレータの作成時にプロトコルが複雑になります。

以下は、常に空のように動作するカスタムイテレータの(愚かな)例です。

class EmptyIterator():
    def __iter__(self): return self
    def __next__(self): raise StopIteration

for blah in EmptyIterator():
    print('this is never reached')
try:
    next(EmptyIterator())
except StopIteration:
    print('Oh hi, I was empty so I raised this Exception for you.')
4
Cody Piersall

これは私には奇妙です。空のコレクションを別の方法で処理する必要があるのはなぜですか?

何かを行う前にコレクションが空であるかどうかをプログラマにチェックさせることは、広範囲にわたる問題のある種類の負担になります。さらに悪いことに、空のコレクションは決してexceptionalです。

個人的には、例外がif empty, do nothingと言うのを忘れたために例外がアプリをクラッシュさせるという時折のバグではなく、空のコレクションで異なる動作が必要になるというまれなケースをチェックするのを忘れたため、何も起こらないことが時々あるバグが欲しいどこでも私はその一般的で予想される動作を望んでいました。

つまり、空の文字列を印刷すると例外がスローされると思いますか?

9
Telastyn

私にとって、空のシーケンスの反復は、(ほぼ)0を追加するか、1を乗算するのと同じカテゴリに分類されます。確かに、値に1を乗算する(または0を追加する)と、何も起こりません。しかし、これは「何もしない」という便利な特殊なケースであり、機能し、アイデンティティー要素が必要なまれなケースでは、それを特殊なケースにする必要がないので便利です。

同様にイテレーション。特別なプロパティを保持しているために選択した一連の事柄があり、それぞれに何かをしたい場合は、通常、「特別なものはない」という特別なケースを許可する方が便利です。何もしないでください。

ただし、この例には1つ問題があります。 print iが何もしないということではなく、決して実行されないということです。そして、それがif not i:が何もしない理由でもあります。ただし、xが[False][None][0]、およびブール値のコンテキストでfalseと見なされる他のいくつかの値である場合は、そうなります。

1
Vatine