Pythonのイテレータを自作する:iterとnextメソッドの実装と仕組み

Pythonの for 文は、リストや辞書だけでなく、あらゆる「イテラブル(反復可能)」なオブジェクトに対してループ処理を行うことができます。

この「イテラブル」の裏側で動いているのが**「イテレータ(Iterator)」**という仕組みです。独自のクラスに特定の特殊メソッドを実装することで、そのオブジェクトを for 文で回せるようにしたり、next() 関数で値を一つずつ取り出せるようにしたりすることができます。

この記事では、イテレータを構成する2つの重要なメソッド __iter____next__ の役割と、その実装方法について解説します。

目次

イテラブルとイテレータの違い

実装に入る前に、用語の整理をします。

  1. イテラブル (Iterable):
    • 「反復可能なオブジェクト」。
    • __iter__ メソッドを持ち、イテレータを返すことができるオブジェクト。
    • 例: リスト、タプル、辞書、文字列など。
  2. イテレータ (Iterator):
    • 「値を順番に取り出す機能を持つオブジェクト」。
    • __next__ メソッドを持ち、**次の値を返す(または終了を通知する)**ことができるオブジェクト。
    • 自分自身もイテラブルである必要があるため、__iter__ メソッドも持ち(通常は自分自身 self を返す)、イテレータとしても振る舞います。

イテレータの実装に必要なメソッド

自作クラスをイテレータにするには、以下の2つの特殊メソッドを実装します。これらを合わせて「イテレータプロトコル」と呼びます。

1. __iter__(self)

イテレータオブジェクト自体(通常は self)を返します。これにより、このオブジェクトが for 文などでイテラブルとして認識されます。

2. __next__(self)

次の要素を返します。取り出す要素がなくなった場合は、StopIteration 例外を送出(raise)して、反復の終了を通知します。

実装例:偶数を返すイテレータ

指定された上限値までの偶数(0, 2, 4…)を順番に返すイテレータクラス EvenNumberIterator を作成します。

class EvenNumberIterator:
    """
    指定された上限値までの偶数を返すイテレータ
    """
    def __init__(self, limit):
        self.limit = limit
        self.current = 0

    def __iter__(self):
        """
        イテレータ自身を返す
        """
        return self

    def __next__(self):
        """
        次の偶数を返す。上限を超えたら終了する。
        """
        # 現在の値が上限を超えていないかチェック
        if self.current > self.limit:
            # 反復終了を通知する例外を送出
            raise StopIteration
        
        # 返すべき値を保持
        value_to_return = self.current
        
        # 次回のために値を更新(+2)
        self.current += 2
        
        return value_to_return

# --- 動作確認 ---

# 10までの偶数を生成するイテレータを作成
even_gen = EvenNumberIterator(10)

print("--- next() 関数での呼び出し ---")
# next() を呼ぶたびに __next__ が実行される
print(next(even_gen)) # 0
print(next(even_gen)) # 2
print(next(even_gen)) # 4

print("\n--- 残りをループで処理 ---")
# for文は StopIteration が出るまで next() を呼び続ける
for num in even_gen:
    print(num)

実行結果:

--- next() 関数での呼び出し ---
0
2
4

--- 残りをループで処理 ---
6
8
10

イテレータの「使い切り」特性

イテレータは、一度最後まで(StopIterationまで)進むと、そこで「枯渇」します。同じイテレータオブジェクトを再度 for 文にかけても、何も出力されません。

# すでに最後まで到達したイテレータ
print("--- 再利用の試行 ---")
for num in even_gen:
    print(num) # 実行されない
print("--- 終了 ---")

実行結果:

--- 再利用の試行 ---
--- 終了 ---

もし、リストのように「何度でもループできる」オブジェクトを作りたい場合は、__iter__ メソッドの中で、新しいイテレータのインスタンスを作成して返すように実装する必要があります(これを「イテラブル」の実装と呼びます)。

まとめ

  • イテレータを自作するには __iter____next__ を実装します。
  • __iter__self を返します。
  • __next__ は次の値を返し、終了時には StopIteration を発生させます。
  • イテレータは状態(現在の位置)を保持するため、一度使い切ると再利用できません。

ジェネレータ(yield)を使えばより簡単に実装できますが、クラスとして実装することで、複雑な状態管理や独自のメソッドを持たせることが可能になります。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次