Pythonのイテレータとイテラブルとは?

Pythonにおけるイテレータ(iterator)とイテラブル(iterable)は、繰り返し処理を支える重要な概念です。これらは、Pythonforループや他の繰り返し構文の基礎を形成しています。例えば、リストやタプル、辞書、文字列などは、すべてイテラブルと呼ばれます。これらのイテラブルオブジェクトから要素を順に取り出すための仕組みが、イテレータです。 この記事では、イテレータとイテラブルの違い、仕組み、実際の使い方、カスタムイテレータの作り方を解説します。

イテラブルとは?

イテラブル(iterable)は、forループや他の繰り返し処理で使用できるオブジェクトのことです。イテラブルオブジェクトには、リスト、タプル、文字列、辞書、セットなどが含まれます。これらは、内部的に__iter__()というメソッドを持っており、このメソッドがイテレータを返します。

イテラブルの例

my_list = [1, 2, 3, 4, 5]
# リストはイテラブル
for item in my_list:
    print(item)

このように、forループを使ってリストの各要素を繰り返し処理できるのは、リストがイテラブルだからです。forループを使うと、Pythonは内部的にmy_list.__iter__()を呼び出し、イテレータを生成しています。

イテラブルの確認方法

イテラブルかどうかを確認するには、iter()関数を使います。iter()が成功すれば、そのオブジェクトはイテラブルです。

my_tuple = (10, 20, 30)
my_iterable = iter(my_tuple)
print(my_iterable)  # 出力: <tuple_iterator object at 0x...>

このコードでは、タプルがイテラブルであるため、iter()関数を使ってイテレータを取得できます。

イテレータとは?

イテレータ(iterator)は、イテラブルから次の要素を順番に取り出すオブジェクトです。イテレータは、__iter__()__next__()という2つのメソッドを実装しており、next()関数を使うことで、1つずつ要素を取得できます。

イテレータの例

次に、イテレータの動作を確認してみましょう。

my_list = [1, 2, 3]
my_iterator = iter(my_list)
# イテレータから順に要素を取得
print(next(my_iterator))  # 出力: 1
print(next(my_iterator))  # 出力: 2
print(next(my_iterator))  # 出力: 3
# 要素がなくなるとStopIteration例外が発生
print(next(my_iterator))  # 出力: StopIteration

ここでは、iter()関数でリストのイテレータを作成し、next()関数で要素を順に取り出しています。すべての要素を取得し終わると、StopIteration例外が発生します。

__next__()__iter__()の役割

  • __iter__(): イテレータオブジェクトそのものを返します。これにより、イテレータはイテラブルとしても機能します。
  • __next__(): イテレータの次の要素を返します。要素がなくなるとStopIterationを発生させます。
class MyIterator:
    def __init__(self, data):
        self.data = data
        self.index = 0
    def __iter__(self):
        return self
    def __next__(self):
        if self.index < len(self.data):
            result = self.data[self.index]
            self.index += 1
            return result
        else:
            raise StopIteration
# MyIteratorクラスの使用例
my_iter = MyIterator([10, 20, 30])
for item in my_iter:
    print(item)

この例では、MyIteratorクラスがカスタムイテレータを定義しています。__next__()メソッドでデータを1つずつ返し、要素が尽きるとStopIterationを発生させています。

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

  • イテラブル: 繰り返し処理ができるオブジェクト。リストやタプル、文字列など。__iter__()メソッドを持ち、このメソッドがイテレータを返す。
  • イテレータ: 次の要素を返すオブジェクト。__next__()メソッドを持ち、要素がなくなるとStopIterationを送出する。イテレータ自身もイテラブルであり、__iter__()メソッドを持つ。

カスタムイテレータの作成

Pythonでは、自分でイテレータを作成することもできます。イテレータを作成するには、__iter__()__next__()メソッドを実装します。

例:カスタムカウンタイテレータ

次に、特定の範囲で動作するカウンタを持つイテレータを実装してみましょう。

class Counter:
    def __init__(self, start, end):
        self.current = start
        self.end = end
    def __iter__(self):
        return self
    def __next__(self):
        if self.current <= self.end:
            result = self.current
            self.current += 1
            return result
        else:
            raise StopIteration
# カスタムカウンタの使用例
counter = Counter(1, 5)
for num in counter:
    print(num)

このCounterクラスは、startからendまでの数値を順に返すイテレータです。__next__()メソッドで次の値を返し、endに達するとStopIterationを送出します。

ジェネレータとイテレータの違い イテレータと似た概念にジェネレータがあります。ジェネレータは、yield文を使って値を返す特殊な関数で、実装が簡単なイテレータの一種です。

ジェネレータの例

def my_generator():
    yield 1
    yield 2
    yield 3
gen = my_generator()
for val in gen:
    print(val)

ジェネレータは内部で自動的にイテレータとして動作し、yieldで次の値を返します。forループで使用すると、イテレータと同じように動作します。

イテレータとイテラブルの活用例

イテレータとイテラブルは、Pythonでの繰り返し処理を効率化する強力なツールです。これらは、大規模なデータセットの処理や、要素を一度にすべて読み込まずに処理する場面で特に役立ちます。

例:ファイルの行をイテレータで処理

with open('data.txt', 'r') as file:
    for line in file:
        print(line.strip())

ファイルオブジェクトはイテラブルなので、forループで1行ずつ処理できます。これにより、メモリ効率よく大きなファイルを扱うことが可能です。

結論

Pythonのイテラブルとイテレータは、繰り返し処理の基盤となる重要な概念です。イテラブルは繰り返し可能なオブジェクトであり、forループで直接利用できます。一方、イテレータはnext()関数で要素を1つずつ取得できるオブジェクトです。 イテレータは、カスタムクラスやデータストリームなどで活用することで、大量のデータ処理や動的なデータ生成に役立ちます。イテラブルとイテレータの仕組みを理解し、適切に使うことで、効率的なPythonプログラムを作成することができるでしょう。