2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ジェネレータ式とyieldによる遅延評価の設計美学

Posted at

概要

大規模なデータ処理や、無限系列、重いI/O処理の最適化において、
「今必要な分だけを評価する」=遅延評価(lazy evaluation) は極めて重要な設計原則である。

Pythonにおける遅延評価の中核が yield とジェネレータ。
本稿ではその構文、実行原理、パフォーマンス面の利点、
さらに設計上の美学としての活用法を解説する。


1. ジェネレータとは?

yield を使って値を「返しつつ一時停止する」関数=ジェネレータ関数

def count_up_to(n):
    i = 1
    while i <= n:
        yield i
        i += 1

for num in count_up_to(3):
    print(num)

出力:

1
2
3
  • 関数内に yield が含まれると、その関数は「ジェネレータオブジェクト」を返す
  • 実行は1回ごとに一時停止し、再開されるたびに次の yield へ進む

2. 遅延評価のメリット

✅ メモリ効率が高い

def read_large_file(path):
    with open(path) as f:
        for line in f:
            yield line

→ ファイル全体を一気に読み込まず、1行ずつ処理可能


✅ 無限列にも対応できる

def infinite():
    i = 0
    while True:
        yield i
        i += 1

→ 通常のリストでは不可能な 無限シーケンスの扱い を安全に実現できる


3. ジェネレータ式(generator expression)

squares = (x**2 for x in range(5))
print(next(squares))  # → 0
  • [] ではなく () を使うことで リスト内包表記の遅延版 を生成
  • mapやfilterとの併用も有効
result = sum(x for x in range(1_000_000) if x % 2 == 0)

不要なリスト化を避けつつ、大規模計算を効率化


4. yield from による委譲

def subgen():
    yield 1
    yield 2

def main():
    yield 0
    yield from subgen()
    yield 3

print(list(main()))  # → [0, 1, 2, 3]
  • ジェネレータ同士の合成・中継に便利
  • ネストを避けて可読性が向上する

5. 状態付き処理・I/Oへの応用

def chunked(iterable, size):
    chunk = []
    for item in iterable:
        chunk.append(item)
        if len(chunk) == size:
            yield chunk
            chunk = []
    if chunk:
        yield chunk

→ 状態(バッファ)を保持しつつ、チャンク単位で処理
→ DBのバルク挿入やバッチ処理に最適


6. 実務での代表パターン

✅ ストリーム系の逐次処理

def parse_lines(lines):
    for line in lines:
        if line.startswith("#"):
            continue
        yield line.strip()

✅ メモリ使用量を抑えたデータフィルタリング

def filter_valid(items):
    return (item for item in items if item.is_valid())

✅ ジェネレータを for だけでなく list() にも渡せる

data = list(chunked(range(10), 3))
# → [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

よくある誤解と対策

yield は戻り値を返すもの?

→ ✅ yield は「返して止まる」、return は「返して終了」
→ 完全に異なる設計意図を持つ


❌ ジェネレータは遅い?

→ ✅ 計算量が重ければむしろジェネレータの方が高速になる
→ イテレーション全体を保持しない分、オーバーヘッドが小さい


yield を使うと複雑になる

→ ✅ 小さな関数単位で切り出すと状態管理が明示的になり、ロジックが明快になる


結語

yield やジェネレータ式は、Pythonにおける**“遅延評価による設計効率化”の本質を体現する機能**である。

  • メモリ効率・I/O最適化・状態付き逐次処理に強く
  • 「処理の完了」ではなく「処理の一時停止」という発想で設計できる
  • コードの柔軟性と再利用性を高める設計原理

Pythonicとは、“すべてを保持せず、必要なときに必要な分だけを処理する美しさ”であり、
ジェネレータはそれを最も象徴的に実現する構文である。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?