1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Python】Python のジェネレータ(Generator)を本質から理解する

1
Posted at

はじめに

Python を学んでいると、ある段階で必ず出会うのが 生成器(Generator) と yield です

def func():
    yield 1

多くの人がここでこう思います。

  • yield って return と何が違うの?
  • 関数なのに途中で止まるってどういうこと?
  • ジェネレータはいつ使うのが正解?

ジェネレータは 文法のトリック ではありません。
これは Python の 設計思想を体現した重要な仕組み です。

本記事では、ジェネレータを 概念 → 仕組み → 実践 の順で解説します。


1. ジェネレータ(Generator)とは何か

一言で言うと

ジェネレータとは「値を1つずつ生成する関数」

より正確には:

yield を含む関数を呼び出すと、イテレータが返る


2. ジェネレータ関数の基本

2.1 最もシンプルなジェネレータ

def gen():
    yield 1
    yield 2
    yield 3

呼び出してみます。

g = gen()

print(next(g))  # 1
print(next(g))  # 2
print(next(g))  # 3
print(next(g))  # StopIteration

ここで重要なのは:

  • gen() を呼んでも 処理は実行されない
  • ジェネレータオブジェクトが返るだけ

3. yield と return の決定的な違い

3.1 return の挙動

def func():
    return 1
    return 2
  • return が実行された瞬間に関数は終了
  • 値は1つしか返せない

3.2 yield の挙動

def gen():
    yield 1
    yield 2
  • yield値を返して処理を一時停止
  • 次の next()続きから再開

生成器は「状態を保存する関数」


4. 実行の流れを可視化する

def demo():
    print("A")
    yield 1
    print("B")
    yield 2
    print("C")
g = demo()

next(g)
# A
# -> 1

next(g)
# B
# -> 2

next(g)
# C
# StopIteration

ポイント

  • 関数のローカル変数
  • 実行位置
  • すべてが保持される

5. ジェネレータはイテレータである

g = gen()

iter(g) is g  # True

ジェネレータは以下を満たします:

  • __iter__() を持つ
  • __next__() を持つ

つまり:

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


6. ジェネレータ式(Generator Expression)

6.1 基本形

gen = (x * x for x in range(5))

対比:

lst = [x * x for x in range(5)]
項目 リスト内包表記 ジェネレータ
評価タイミング 即時 遅延
メモリ使用 多い 少ない
再利用 可能 不可

7. なぜ生成器が重要なのか

7.1 メモリ効率が圧倒的に良い

def count_up(n):
    for i in range(n):
        yield i
for x in count_up(10**9):
    ...
  • 10億個のデータを保持しない
  • 必要な分だけ生成

7.2 ファイル・ログ処理

def read_lines(path):
    with open(path) as f:
        for line in f:
            yield line.strip()
  • 大容量ログでも安全
  • ストリーム処理が自然に書ける

7.3 パイプライン処理(Pythonic)

def numbers():
    for i in range(10):
        yield i

def even(nums):
    for n in nums:
        if n % 2 == 0:
            yield n

def square(nums):
    for n in nums:
        yield n * n

for x in square(even(numbers())):
    print(x)

データを「流す」設計


8. よくある落とし穴

8.1 生成器は一度しか使えない

g = (x for x in range(3))

list(g)  # [0, 1, 2]
list(g)  # []

8.2 return は即終了

def gen():
    yield 1
    return 2
  • return は生成器を終了させる
  • 2StopIteration(2) に入る(通常使わない)

9. for 文と生成器の関係

for x in gen():
    print(x)

内部的には:

it = iter(gen())
while True:
    try:
        x = next(it)
        print(x)
    except StopIteration:
        break

ジェネレータは for 文の主役


10. いつジェネレータを使うべきか

ジェネレータが向いている場面

  • データ量が大きい
  • 一度しか使わない
  • ファイル・ネットワーク・ストリーム
  • パフォーマンス・省メモリ重視

向いていない場面

  • ランダムアクセスが必要
  • 複数回使い回す
  • 小規模データ

おわりに

生成器は Python における「流れる思考」そのもの

  • データを溜めない
  • 必要な分だけ作る
  • 状態を自動で管理する

ジェネレータを自然に使えるようになると、
Python のコードは 短く・速く・美しく なります。

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?