Python でクラスを使ったカスタムイテレータを作る
クラスを使ってカスタムイテレータを作ると、複雑な状態管理やカスタムロジックを持つイテレータを実装できます。ここでは実践的な例をいくつか紹介します。
範囲イテレータ
Python の range() のような機能を持つイテレータを作ってみましょう。
class MyRange:
def __init__(self, start, stop, step=1):
self.start = start
self.stop = stop
self.step = step
def __iter__(self):
current = self.start
while current < self.stop:
yield current
current += self.step
for n in MyRange(0, 10, 2):
print(n, end=" ")
# 0 2 4 6 8
逆順イテレータ
リストを逆順でイテレートするクラスです。
class Reversed:
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index -= 1
return self.data[self.index]
for char in Reversed("hello"):
print(char, end="")
# olleh
サイクルイテレータ
要素を無限に繰り返すイテレータです。
class Cycle:
def __init__(self, iterable):
self.items = list(iterable)
self.index = 0
def __iter__(self):
return self
def __next__(self):
if not self.items:
raise StopIteration
item = self.items[self.index]
self.index = (self.index + 1) % len(self.items)
return item
colors = Cycle(["red", "green", "blue"])
for i, color in enumerate(colors):
if i >= 7:
break
print(color, end=" ")
# red green blue red green blue red
ファイル行イテレータ
ファイルを特定の条件でフィルタリングしながら読み込むイテレータです。
class NonEmptyLines:
def __init__(self, filename):
self.filename = filename
self.file = None
def __iter__(self):
self.file = open(self.filename, 'r')
return self
def __next__(self):
while True:
line = self.file.readline()
if not line:
self.file.close()
raise StopIteration
line = line.strip()
if line: # 空行をスキップ
return line
# 使用例
# for line in NonEmptyLines("data.txt"):
# print(line)
ペアイテレータ
隣り合う要素をペアで返すイテレータです。
class Pairwise:
def __init__(self, iterable):
self.iterator = iter(iterable)
self.prev = None
self.started = False
def __iter__(self):
return self
def __next__(self):
if not self.started:
self.prev = next(self.iterator)
self.started = True
current = next(self.iterator)
result = (self.prev, current)
self.prev = current
return result
numbers = [1, 2, 3, 4, 5]
for pair in Pairwise(numbers):
print(pair)
# (1, 2)
# (2, 3)
# (3, 4)
# (4, 5)
設計のポイント
状態管理
インスタンス変数で現在位置や状態を管理する。__next__() で状態を更新する。
再利用性
同じオブジェクトを複数回イテレートしたい場合は、__iter__() で新しいイテレータを返す設計にする。
クラスベースのイテレータは柔軟性が高いですが、シンプルなケースではジェネレータ関数を使う方が簡潔です。