Pythonでは、コレクション内の要素を順に処理する仕組みとしてイテレータ(iterator)とジェネレータ(generator)が提供されています。これらはメモリ効率やコードの可読性を高める上で重要な役割を果たします。
イテレータの基本
イテレータは、現在の走査位置を記憶できるオブジェクトであり、__iter__() と __next__() の2つの特殊メソッドを実装することでカスタムイテレータを作成できます。
組み込み型(リスト、タプル、文字列など)は既にイテラブルであり、iter() を使ってイテレータに変換可能です:
data = [10, 20, 30]
it = iter(data)
print(next(it)) # 10
print(next(it)) # 20
また、for ループでも自然に使用できます:
for val in iter([10, 20, 30]):
print(val, end=' ') # 出力: 10 20 30
カスタムイテレータの実装
クラスでイテレータを実装するには、以下の例のように __iter__() と __next__() を定義します:
class Counter:
def __init__(self, limit):
self.limit = limit
def __iter__(self):
self.current = 0
return self
def __next__(self):
if self.current < self.limit:
value = self.current
self.current += 1
return value
else:
raise StopIteration
# 使用例
counter = Counter(3)
for num in counter:
print(num) # 0, 1, 2
StopIteration 例外は、イテレーションの終了を示すために必須です。これにより無限ループを回避できます。
ジェネレータによる遅延評価
ジェネレータは、yield キーワードを含む関数として定義され、呼び出されるとイテレータを返します。実行は yield で一時停止し、次回の呼び出し時にその続きから再開されます。
以下は、数値を逆順に生成するシンプルなジェネレータの例です:
def reverse_count(start):
while start > 0:
yield start
start -= 1
gen = reverse_count(4)
print(next(gen)) # 4
print(next(gen)) # 3
# 残りをforループで取得
for n in gen:
print(n) # 2, 1
フィボナッチ数列のジェネレータ実装
ジェネレータは無限または大きなシーケンスをメモリ効率よく扱うのに適しています。以下はフィボナッチ数列を生成する例です:
def fib_sequence(n_terms):
a, b = 0, 1
count = 0
while count < n_terms:
yield a
a, b = b, a + b
count += 1
# 最初の10項を出力
for val in fib_sequence(10):
print(val, end=' ') # 0 1 1 2 3 5 8 13 21 34
このように、ジェネレータは必要になるまで値を生成しないため、大規模データ処理において非常に有用です。