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__() で新しいイテレータを返す設計にする。

クラスベースのイテレータは柔軟性が高いですが、シンプルなケースではジェネレータ関数を使う方が簡潔です。