web-dev-qa-db-ja.com

Pythonで「反復可能」とはどういう意味ですか? `__getitem __()`を実装するオブジェクトが反復可能でないのはなぜですか?

最初に明確にしたいのは、「イテレータ」とは何なのかということではありません。

これは、「反復可能」という用語がPythonの doc で定義されている方法です。

反復可能

一度に1つのメンバーを返すことができるオブジェクト。反復可能オブジェクトの例には、すべてのシーケンス型(リスト、str、タプルなど)と一部の非-dictのようなシーケンスタイプ、ファイルオブジェクト、__ iter __()または__getitem __()メソッドで定義したクラスのオブジェクト。

イテラブルは、forループや、シーケンスが必要な他の多くの場所(Zip()、map()など)で使用できます。反復可能なオブジェクトが引数として組み込み関数iter()に渡されると、オブジェクトの反復子が返されます。この反復子は、値のセットを1回パスするのに適しています。イテラブルを使用する場合、通常はiter()を呼び出したり、イテレータオブジェクトを自分で処理したりする必要はありません。 forステートメントはそれを自動的に行い、ループの期間中イテレータを保持する一時的な名前のない変数を作成します。

イテレータ、シーケンス、ジェネレータもご覧ください。

他の人が示唆したように のように、isinstance(e, collections.Iterable)を使用することは、オブジェクトが反復可能かどうかを確認する最もPython的な方法です。
そこでPython 3.4.3:

from collections.abc import Iterable

class MyTrain:
    def __getitem__(self, index):
        if index > 3:
            raise IndexError("that's enough!")

        return index

for name in MyTrain():
    print(name)  # 0, 1, 2, 3

print(isinstance(MyTrain(), Iterable))  # False

結果はかなり奇妙です:MyTrain__getitem__メソッドを定義していますが、一度に1つの数値を返すことができることは言うまでもなく、反復可能なオブジェクトとは見なされません。

次に、__getitem__を削除し、__iter__メソッドを追加しました。

from collections.abc import Iterable

class MyTrain:    
    def __iter__(self):
        print("__iter__ called")
        pass

print(isinstance(MyTrain(), Iterable))  # True

for name in MyTrain():
    print(name)  # TypeError: iter() returned non-iterator of type 'NoneType'

反復中に何も生成できないにもかかわらず、「真の」反復可能オブジェクトと見なされます。

それで私は何かを誤解しましたか、それともドキュメントが間違っていますか?

9
laike9m

ここでの混乱のポイントは、__getitem__doesを実装してもオブジェクトを反復できることですが、それは定義されているインターフェースの一部ではありません Iterable

抽象基本クラス は、仮想サブクラス化の形式を許可します。指定されたメソッドを実装するクラス(Iterableの場合、__iter__のみ)はisinstanceissubclassをABCのサブクラスにする明示的に継承しない場合でも。ただし、メソッドの実装実際に機能するかどうかは確認されませんが、提供されているかどうかは確認されません。

詳細については、ABCを導入した PEP-3119 を参照してください。


isinstance(e, collections.Iterable)の使用は、オブジェクトが反復可能かどうかを確認する最もPython的な方法です

同意しません;私は duck-typingだけを使用してオブジェクトを反復処理します。オブジェクトが反復可能でない場合、TypeErrorが発生します。これは、反復不可能な入力を処理する場合は関数でキャッチでき、そうでない場合は呼び出し元までパーコレートすることができます。これにより、オブジェクトが反復を実装することを決定した方法が完全に回避され、最適なタイミングでそれが実行されるかどうかがわかります。


もう少し追加すると、あなたが引用したドキュメントはわずかに誤解を招くと思います。 iter docs を引用すると、おそらくこれが明確になります。

objectは、反復プロトコル(__iter__()メソッド)をサポートするコレクションオブジェクトであるか、シーケンスプロトコル(__getitem__()メソッドで始まり、 0)。

これにより、どちらのプロトコルでもオブジェクトを反復可能にしても、実際の"iteration protocol"は1つだけであり、isinstance(thing, Iterable)がテストするのはこのことです。したがって、最も一般的なケースで"反復できるもの"を確認する1つの方法は次のようになると結論付けることができます。

isinstance(thing, (Iterable, Sequence))

ただし、これには__len____getitem__とともに"virtually sub-class"Sequenceに実装する必要もあります。

3
jonrsharpe

それis反復可能です。ただし、abc.Iterableから継承していないため、当然Pythonは、そのクラスの子孫であるとは報告しません。反復可能であること、およびそのベースからの子孫の2つクラス-まったく別です。

0
Daniel Roseman