イテレータ・ジェネレータを日本一わかりやすく解説!メモリ効率が劇的に向上

 

イテレータとジェネレータって何?5秒で理解

イテレータ:一つずつ要素を取り出せるオブジェクト
ジェネレータ:必要な時だけ値を作るメモリ効率の良いイテレータ

簡単に言うと、大量のデータを一度にメモリに読み込まず、必要な分だけ処理する仕組みです。

なぜイテレータ・ジェネレータが重要なのか?

メリット

  • メモリ効率抜群:100万件のデータでもメモリ使用量は最小限
  • 高速処理:必要な分だけ処理するので無駄がない
  • 大容量データ対応:メモリに入りきらないデータも処理可能
  • 遅延評価:実際に使う時まで計算を遅らせる

イテレータとは?基礎から理解

イテレータの基本概念

イテレータとは、next()関数で次の要素を取得できるオブジェクトです。

# リストをイテレータに変換
my_list = [1, 2, 3]
iterator = iter(my_list)
print(next(iterator))  # 1
print(next(iterator))  # 2

イテレータの作成方法

class CountUp:
    def __init__(self, max_num):
        self.max_num = max_num
        self.num = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.num < self.max_num:
            self.num += 1
            return self.num
        raise StopIteration

# 使用例
counter = CountUp(3)
for num in counter:
    print(num)  # 1, 2, 3

ジェネレータとは?革命的な機能

ジェネレータ関数の基本

yieldキーワードを使うことで、簡単にジェネレータが作れます。

def count_up(max_num):
    num = 0
    while num < max_num:
        num += 1
        yield num

# 使用例
for num in count_up(3):
    print(num)  # 1, 2, 3

メモリ効率の比較

# 通常のリスト(メモリを大量消費)
def create_list(n):
    return [x**2 for x in range(n)]

# ジェネレータ(メモリ効率抜群)
def create_generator(n):
    for x in range(n):
        yield x**2

# 100万個の要素でも軽々処理
gen = create_generator(1000000)  # メモリ使用量:最小限

ジェネレータ式:内包表記の進化版

基本構文

# リスト内包表記
squares_list = [x**2 for x in range(5)]

# ジェネレータ式(括弧を使用)
squares_gen = (x**2 for x in range(5))

実践例:ファイル処理

# 大きなファイルを効率的に処理
def process_large_file(filename):
    with open(filename) as f:
        return (line.strip().upper() for line in f)

# メモリ効率よく1行ずつ処理
lines = process_large_file('huge_file.txt')
for line in lines:
    print(line)

yield文の詳細解説

yield vs return の違い

# return:一度だけ値を返して終了
def normal_function():
    return 1
    return 2  # 実行されない

# yield:何度でも値を返せる
def generator_function():
    yield 1
    yield 2
    yield 3

gen = generator_function()
print(next(gen))  # 1
print(next(gen))  # 2

yieldの状態保持機能

def fibonacci():
    a, b = 0, 1
    while True:
        yield b
        a, b = b, a + b

# フィボナッチ数列を無限に生成
fib = fibonacci()
for _ in range(5):
    print(next(fib))  # 1, 1, 2, 3, 5

実践的な使用例

1. CSVファイルの大容量処理

def read_csv_generator(filename):
    with open(filename) as f:
        for line in f:
            yield line.strip().split(',')

# 1GB以上のCSVファイルでもメモリ効率よく処理
for row in read_csv_generator('big_data.csv'):
    process_row(row)

2. データベースのページング処理

def fetch_data_pages(page_size=1000):
    offset = 0
    while True:
        data = fetch_from_db(offset, page_size)
        if not data:
            break
        yield from data
        offset += page_size

# 大量データを効率的に処理
for record in fetch_data_pages():
    process_record(record)

3. 無限シーケンスの生成

def infinite_primes():
    yield 2
    primes = [2]
    candidate = 3
    while True:
        if all(candidate % p != 0 for p in primes):
            primes.append(candidate)
            yield candidate
        candidate += 2

# 必要な分だけ素数を取得
primes = infinite_primes()
first_ten = [next(primes) for _ in range(10)]

よくある使い方とパターン

yield from:ジェネレータの委譲

def generator1():
    yield 1
    yield 2

def generator2():
    yield 3
    yield 4

def combined():
    yield from generator1()
    yield from generator2()

list(combined())  # [1, 2, 3, 4]

条件付きジェネレータ

def even_squares(n):
    for x in range(n):
        if x % 2 == 0:
            yield x**2

list(even_squares(6))  # [0, 4, 16]

パフォーマンス比較:実際の数値で確認

メモリ使用量の違い

import sys

# リスト:大量のメモリ使用
big_list = [x for x in range(100000)]
print(sys.getsizeof(big_list))  # 約800KB

# ジェネレータ:最小限のメモリ使用
big_gen = (x for x in range(100000))
print(sys.getsizeof(big_gen))   # 約128バイト

処理速度の違い

import time

# リスト作成:時間がかかる
start = time.time()
big_list = [x**2 for x in range(1000000)]
print(f"リスト作成時間: {time.time() - start:.2f}秒")

# ジェネレータ作成:瞬時
start = time.time()
big_gen = (x**2 for x in range(1000000))
print(f"ジェネレータ作成時間: {time.time() - start:.6f}秒")

エラーハンドリングと注意点

StopIteration例外

def simple_gen():
    yield 1
    yield 2

gen = simple_gen()
print(next(gen))  # 1
print(next(gen))  # 2
# print(next(gen))  # StopIteration例外が発生

ジェネレータの一回限りの性質

gen = (x for x in range(3))
print(list(gen))  # [0, 1, 2]
print(list(gen))  # [] ← もう空!

まとめ:使い分けのポイント

いつ使うべきか?

イテレータを使う場面

  • カスタムなデータ構造を作る時
  • 複雑な状態管理が必要な時

ジェネレータを使う場面

  • 大量データの処理
  • メモリ効率を重視する時
  • 無限シーケンスが必要な時
  • ファイル処理やデータベース処理

パフォーマンス指針

  • 小さなデータ(1000件未満):リストでOK
  • 中程度のデータ(1万件程度):ジェネレータ推奨
  • 大容量データ(10万件以上):ジェネレータ必須

実践演習問題

  1. 基礎問題:1から10までの偶数を返すジェネレータ関数を作成
  2. 応用問題:フィボナッチ数列で100未満の数値のみを返すジェネレータ
  3. 実践問題:テキストファイルから空行を除外して返すジェネレータ

イテレータとジェネレータをマスターすれば、Pythonプログラムのメモリ効率とパフォーマンスが劇的に向上します。まずは小さな例から始めて、徐々に実践的な場面で活用してみましょう!

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座