Pythonのfor-inループは、特別な2つのメソッド__iter__()と__next__()に基づくイテレーションプロトコルを通じて実装されています。ループが始まると、Pythonはiter(obj)を呼び出し(__iter__メソッドを探索)、イテレータが返されると、その__next__()メソッドを呼び出して次の要素を取得します。
要素が正常に発行されると、イテレーションは続きます。StopIteration例外が発生すると、ループは終了します。
カスタムイテレータの例:
class Counter: def __init__(self, low, high): self.current = low self.high = high def __iter__(self): return self def __next__(self): if self.current > self.high: raise StopIteration else: self.current += 1 return self.current - 1 for num in Counter(1, 3): print(num) # 1, 2, 3が出力されます
注意点: 多くの標準Python構造体はイテレータプロトコルを実装しており、あなた自身のオブジェクトも同様にできます。
Pythonの任意のオブジェクトでfor-inループを使用できますか?イテラブルオブジェクトとイテレータの違いは何ですか?
回答: いいえ、for-inループはオブジェクトがイテラブルである必要があり、つまり__iter__()メソッドを実装し、イテレータ(__next__()メソッド付きのオブジェクト)を返す必要があります。イテレータは__iter__()を通じて自分自身を返しますが、イテラブルオブジェクトはそうではありません。
例:
lst = [1, 2, 3] iterator = iter(lst) print(hasattr(lst, '__iter__')) # True print(hasattr(iterator, '__next__')) # True (イテレータ) print(hasattr(lst, '__next__')) # False (イテレータではない)
ストーリー
プロジェクト: 大きなファイルの処理。
問題: プロジェクトでは、ファイルオブジェクトを2回繰り返し(ファイルの行に対してイテレート)ようとしましたが、新しいファイルを明示的に開かないまま、2回目のイテレーションでは何も得られませんでした(ポインタはすでにファイルの最後にあります!)。
ストーリー
プロジェクト: 自分のコレクションクラスをフレームワークに統合。
問題: クラスは
__getitem__のみを実装し、__iter__を実装していなかったため、ジェネレーターや標準のループ(for)と互換性がありませんでした。そのため、イテレータを期待していたサードパーティライブラリが破損しました。
ストーリー
プロジェクト: カスタムコレクションでのmap/filterの使用。
問題: 自分のリストクラスは
__iter__と__next__を実装せず、多くの標準関数と互換性がなく、統合テスト後にのみ明らかになり、大規模なリファクタリングが必要でした。