<一覧に戻る

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

オブジェクト指向プログラミング(OOP)の中でも、イテレータジェネレータは、データの反復処理を効率的かつ柔軟に行うための強力な機能です。このセクションでは、Pythonにおけるイテレータとジェネレータの基本概念、使い方、そしてその違いについて、初心者にもわかりやすく解説します。

イテレータとは?

イテレータは、Pythonでオブジェクトを一つずつ順番に取得するための仕組みです。イテレータは、__iter__() メソッドと __next__() メソッドを持つオブジェクトで、for ループなどの反復処理で利用されます。

イテレータの基本構造

イテレータは、以下の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

説明

  • __init__ メソッド:
  • イテレータの上限値と現在の値を初期化します。

  • __iter__ メソッド:

  • イテレータ自身を返します。これにより、イテレータがイテラブルオブジェクトとして振る舞います。

  • __next__ メソッド:

  • 現在の値が上限値に達していなければ、値を増加させて返します。
  • 上限値に達すると、StopIteration 例外を発生させ、反復を終了します。

ジェネレータとは?

ジェネレータは、イテレータを簡潔に作成するための方法で、特に大きなデータセットや無限シーケンスを扱う際に便利です。ジェネレータは、yield キーワードを使用して値を一つずつ生成します。

ジェネレータの基本構造

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

サンプルコード

以下に、ジェネレータを使用して数値を生成する例を示します。

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

説明

  • ジェネレータ関数:
  • yield キーワードを使用して値を一つずつ生成します。
  • ジェネレータ関数は、一度に全ての値をメモリに保持する必要がないため、メモリ効率が高いです。

  • ジェネレータオブジェクト:

  • ジェネレータ関数を呼び出すと、ジェネレータオブジェクトが返されます。
  • for ループなどの反復処理で使用することで、値を順番に取得できます。

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

特徴 イテレータ ジェネレータ
実装の複雑さ 比較的複雑(クラスの定義が必要) 簡潔(関数内で yield を使用)
メモリ効率 低い場合がある(全ての要素を保持) 高い(必要な要素だけを生成)
状態の管理 明示的に管理 Python が自動で管理
柔軟性 高い(カスタムロジックの実装が可能) 高い(シンプルなロジックに適)

実践例:ファイルの内容を逐次読み込む

大きなファイルを読み込む際に、イテレータとジェネレータを使用して効率的に処理する方法を示します。

イテレータを使用した例

class FileIterator:
    def __init__(self, filename):
        self.file = open(filename, 'r')

    def __iter__(self):
        return self

    def __next__(self):
        line = self.file.readline()
        if line:
            return line.strip()
        else:
            self.file.close()
            raise StopIteration

使用例

# FileIteratorクラスのインスタンスを生成
file_iter = FileIterator('sample.txt')

# イテレータを使用してファイルを読み込む
for line in file_iter:
    print(line)

ジェネレータを使用した例

def file_generator(filename):
    with open(filename, 'r') as file:
        for line in file:
            yield line.strip()

使用例

# ジェネレータ関数を呼び出してジェネレータオブジェクトを取得
gen = file_generator('sample.txt')

# ジェネレータを使用してファイルを読み込む
for line in gen:
    print(line)

説明

  • イテレータの例:
  • クラスを定義してファイルを逐次読み込みます。
  • 各行を順番に返し、ファイルの終わりに達すると StopIteration を発生させます。

  • ジェネレータの例:

  • 関数内で yield を使用してファイルの各行を逐次生成します。
  • with 文を使用してファイルを開くため、ファイルのクローズ処理も自動的に行われます。

まとめ

  • イテレータは、クラスを用いて反復処理を実装する手法で、__iter__()__next__() メソッドを定義する必要があります。
  • ジェネレータは、yield キーワードを使用して簡潔に反復処理を実装でき、メモリ効率が高くなります。
  • ジェネレータは、特に大きなデータセットや無限シーケンスを扱う際に有効です。
  • イテレータとジェネレータを適切に使い分けることで、Pythonの反復処理をより効率的かつ柔軟に行うことができます。
  • 実際にコードを書いて試すことで、イテレータとジェネレータの概念とその使い方をより深く理解しましょう。

参考資料

出力結果: