<一覧に戻る

イテレータとジェネレータ

Pythonを学んでいると必ず出てくる言葉にイテレータジェネレータがあります。

これらはどちらもデータを順番に取り出して処理するための仕組みですが、聞いただけだと難しそうに感じる人も多いはずです。 実際、プログラミング初心者の段階ではfor文を使ってループできれば十分なのでは?と感じる方も多いでしょう。

しかし、データ量が大きくなったり、処理を効率化したいときには、このイテレータジェネレータがとても役立ちます。

今回は、この2つの仕組みを初心者でも理解しやすいように、実際のコード例を交えながら丁寧に解説していきます。

イテレータとは?

まずはイテレータから見ていきましょう。

イテレータという言葉は聞き慣れないかもしれませんが、実は皆さんがすでに使っているfor文の裏側ではイテレータが活躍しています。

イテレータはデータを一つずつ順番に取り出すことができるオブジェクトです。

たとえばリストや文字列などをforループで回すと、Pythonが内部的にイテレータを作って、要素を1つずつ取り出しています。

自分でイテレータを作るときには、__iter__()__next__()という2つの特別なメソッドを定義する必要があります。

イテレータの基本構造

イテレータは、以下の2つのメソッドを実装する必要があります。

  • __iter_(): イテレータ自身を返します。
  • __next__(): 次の要素を返します。要素が無くなるとStopIteration例外を発生させます。

以下に、カスタムイテレータを実装する例を示します。

class MyIterator:
    def __init__(self, limit):
        self.limit = limit  # イテレータの上限値
        self.current = 0    # 現在の値

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

    def __next__(self):
        if self.current < self.limit:
            self.current += 1
            return self.current
        else:
            raise StopIteration  # 反復の終了を通知

# MyIteratorクラスのインスタンスを生成
my_iter = MyIterator(5)

# イテレータの使用
for num in my_iter:
    print(num)

以下のように出力されます。

# 出力:
1
2
3
4
5

ここで重要なのは、next()が呼ばれるたびに値を一つずつ返している点です。 そして上限に達するとStopIterationという例外を発生させ、ループを終了させます。

普段のfor文では意識する必要がありませんが、実際にはこのような仕組みが働いているのです。

ジェネレータとは?

次にジェネレータを見ていきましょう。

ジェネレータはイテレータをもっと簡単に作れる仕組みです。イテレータをクラスで書こうとすると、__iter__や__next__を定義する必要がありましたが、ジェネレータを使えば数行で同じ処理が実現できます。

ジェネレータのキーワードはyieldです。

通常の関数であればreturnを使って値を返しますが、ジェネレータ関数ではyieldを使って値を一つずつ返していきます。

ジェネレータの基本構造

ジェネレータは、通常の関数と同じように定義しますが、値を返す代わりに yield を使用します。 ジェネレータ関数を呼び出すと、ジェネレータオブジェクトが返されます。

イテレータと同じく「1から指定した数までを順に返す」処理をジェネレータで書いてみましょう。

def my_generator(limit):
    current = 0
    while current < limit:
        current += 1
        yield current  # 値を生成し、一時停止

# ジェネレータ関数を呼び出してジェネレータオブジェクトを取得
gen = my_generator(5)

# ジェネレータの使用
for num in gen:
    print(num)

以下のように出力されます。

# 出力:
1
2
3
4
5

たった数行のコードで、イテレータと同じ動作をする仕組みが作れたことが分かります。

イテレータとジェネレータの違い

ここで、両者の違いを分かりやすく表にまとめてみます。

特徴 イテレータ ジェネレータ
実装の複雑さ クラスを定義し、__iter__と__next__を用意する必要がある 関数内でyieldを使うだけで簡単に作れる
メモリ効率 要素をすべて保持することも多く、効率が悪い場合がある 必要になったときだけ値を生成するので効率的
状態の管理 自分で「どこまで処理したか」を記録する必要がある Pythonが自動で状態を保持してくれる
柔軟性 複雑な反復処理や特別なロジックを持つ場合 シンプルに順番に値を返す場合や大規模データ処理

表で見ると、ジェネレータがシンプルで効率的に見えるかもしれません。

実際、初心者が最初に使うならジェネレータの方が分かりやすいでしょう。ただし、イテレータはより細かい制御が可能なので、ケースによってはイテレータが必要になることもあります。

まとめ

Pythonのイテレータとジェネレータは、一見難しそうに思えますが「データを一つずつ順番に取り出す仕組み」と考えるとシンプルです。

  • イテレータはクラスを使って作り込む方法で、細かい制御が可能。
  • ジェネレータはyieldを使った関数で、手軽に効率よく繰り返し処理ができる。

大きなデータを扱うときや、効率的にループを回したいときには特に役立ちます。 最初はジェネレータを使って感覚をつかみ、必要になったときにイテレータの仕組みを使いこなせるようになると、Pythonでの開発の幅が一気に広がります。

出力結果: