Caution

お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。

Python辞典

  1. トップページ
  2. Python辞典
  3. yield / ジェネレータ関数

yield / ジェネレータ関数

『yield』を使った関数はジェネレータ関数となり、呼び出すとジェネレータオブジェクトを返します。ジェネレータは要素を一度にメモリに展開せず、必要なときに1つずつ生成します(遅延評価)。大量のデータを扱う場合はリストより大幅にメモリを節約できます

構文
# ジェネレータ関数(yieldを含む関数)
def my_generator():
    yield 値1
    yield 値2

# ジェネレータ式
gen = (式 for 変数 in イテラブル)

# yield from(別のイテラブルに委譲)
def delegating_gen():
    yield from other_generator()
構文一覧
構文概要
yield 値値を呼び出し元に返し、その場で処理を一時停止する。
yield from iterable別のイテラブル(ジェネレータ含む)の各要素をyieldする。
(式 for x in iter)ジェネレータ式。リスト内包表記の[]を()にした省メモリ版。
next(gen)ジェネレータから次の値を1つ取り出す。
list(gen)ジェネレータの全要素をリストに展開する。
サンプルコード
# 基本的なジェネレータ関数
def count_up(start, end):
    """startからendまでの数値を1つずつ生成する"""
    current = start
    while current <= end:
        yield current
        current += 1

gen = count_up(1, 5)
print(next(gen))    # 1
print(next(gen))    # 2
print(next(gen))    # 3

for n in count_up(1, 5):
    print(n, end=' ')   # 1 2 3 4 5

# メモリ効率の比較
import sys

# リスト: すべてをメモリに保持
big_list = [x ** 2 for x in range(10**6)]
print(sys.getsizeof(big_list))  # 約8MB以上

# ジェネレータ: 遅延評価で省メモリ
big_gen = (x ** 2 for x in range(10**6))
print(sys.getsizeof(big_gen))   # 約120バイト(常に一定)

# yield from: イテラブルへの委譲
def flatten(nested):
    """ネストされたリストをフラット化するジェネレータ"""
    for item in nested:
        if isinstance(item, list):
            yield from flatten(item)    # 再帰的にフラット化
        else:
            yield item

data = [1, [2, 3], [4, [5, 6]], 7]
print(list(flatten(data)))  # [1, 2, 3, 4, 5, 6, 7]

# 無限ジェネレータ(イテラブルと組み合わせて使う)
def fibonacci():
    """フィボナッチ数列を無限に生成する"""
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

import itertools
first_10 = list(itertools.islice(fibonacci(), 10))
print(first_10)  # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

# ジェネレータ式のチェーン
lines = ['  hello  ', '  world  ', '  python  ']
stripped = (line.strip() for line in lines)
upper    = (s.upper() for s in stripped)
print(list(upper))  # ['HELLO', 'WORLD', 'PYTHON']
概要

ジェネレータはイテレータプロトコル(__iter__と__next__)を自動的に実装したオブジェクトです。『yield』に到達すると処理が一時停止し、次回のnext()呼び出しでyieldの直後から再開します。StopIterationが送出されるとイテレーションが終了します。

『yield from』はPython 3.3で追加されました。別のジェネレータやイテラブルに処理を委譲するときに使い、ネストしたfor文でyieldするより簡潔に書けます。また非同期プログラミング(async/await)の基礎になっており、コルーチンとしても使えます。

一度消費したジェネレータは再利用できません。再度ループしたい場合はリストに変換するか、ジェネレータ関数を再度呼び出す必要があります。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。