問題の歴史: 反復可能性の概念は、Pythonにおいてコレクション(リスト、辞書、集合など)を統一して操作するために登場しました。forループを使用してループ処理できる任意のオブジェクトは、反復可能と見なされます。これは特定の魔法のメソッドを介して実現されています。
問題: Pythonは、シーケンスに関連するループや関数の正しい動作のために特定のプロトコルを要求します。ユーザーが自分のクラスでこれらのプロトコルを正しく実装しないと、標準のメカニズム(for、list()、sum()など)が機能しないか、予期しない動作をすることがあります。
解決策:
反復可能なのは、__iter__メソッドを実装しているオブジェクトです。イテレータは、__next__メソッドとselfを返す__iter__メソッドを持つオブジェクトです。通常、__iter__から返されるオブジェクトはイテレータですが、必ずしもそうではありません。例:
class MyRange: def __init__(self, start, end): self.start = start self.end = end def __iter__(self): self.current = self.start return self def __next__(self): if self.current < self.end: val = self.current self.current += 1 return val raise StopIteration for x in MyRange(1, 4): print(x) # 1, 2, 3
重要な特徴:
__iter__メソッドの有無によって決まります。__next__とselfを返す__iter__の両方を実装する必要があります。__iter__で新しいオブジェクトを返すことで、コレクションに対して複数の独立した巡回を効率的に実現できます。すべての反復可能オブジェクトに__next__メソッドは必要ですか?
いいえ。反復可能オブジェクトには、イテレータを返す__iter__のみが必要です。__next__は、イテレータ自体が実装します。例えば、listには__next__メソッドがありませんが、反復可能です:その__iter__はイテレータのインスタンスを返します。
lst = [1, 2, 3] print(hasattr(lst, '__next__')) # False
オブジェクトはそれ自体でイテレータになれますか?
はい、両方のメソッド__iter__と__next__を実装している場合はそうです。
1つのコレクションに対して独立した状態の複数のイテレータを作成できますか?
はい、__iter__が毎回新しいイテレータオブジェクトを返す場合は可能です。
class MyList: def __init__(self, data): self.data = data def __iter__(self): return iter(self.data)
__iter__なしで__next__を実装する(またはその逆)。ネガティブケース: イテレータクラスが状態(例えば、現在のインデックス)をクラスレベルで保持し、インスタンスレベルではないため、並行して巡回時に互いに壊れてしまいます。 利点:
ポジティブケース:
各イテレータは、__iter__で作成されたインスタンスにその状態を保持します。
利点: