Python の yield from で委譲する

yield from は、別のイテラブルやジェネレータの要素をすべて委譲して yield するための構文です。ネストしたジェネレータを扱う際にコードを簡潔にできます。

yield from の基本

yield from を使うと、イテラブルの各要素を順番に yield できます。

def generator():
    yield from [1, 2, 3]
    yield from "ABC"

for value in generator():
    print(value)
# 1, 2, 3, A, B, C

yield from を使わない場合との比較

yield from がない場合、for ループを書く必要があります。

yield from を使う

yield from iterable
コードが簡潔になる

for ループを使う

for item in iterable:
yield item
冗長になる

# yield from なし
def flatten_without():
    for item in [1, 2, 3]:
        yield item
    for item in [4, 5, 6]:
        yield item

# yield from あり
def flatten_with():
    yield from [1, 2, 3]
    yield from [4, 5, 6]

ネストしたリストのフラット化

yield from の典型的な使い方は、入れ子になった構造をフラットにすることです。

def flatten(nested):
    for item in nested:
        if isinstance(item, list):
            yield from flatten(item)  # 再帰的に委譲
        else:
            yield item

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

サブジェネレータへの委譲

yield from は別のジェネレータに処理を委譲する際にも使います。

def sub_generator():
    yield "sub 1"
    yield "sub 2"
    return "サブ完了"

def main_generator():
    yield "main 開始"
    result = yield from sub_generator()
    print(f"受け取った値: {result}")
    yield "main 終了"

for value in main_generator():
    print(value)
# main 開始
# sub 1
# sub 2
# 受け取った値: サブ完了
# main 終了

サブジェネレータの return 値は、yield from 式の結果として受け取れます。

複数のデータソースを連結

複数のイテラブルを順番に処理するジェネレータを簡単に作れます。

def chain(*iterables):
    for it in iterables:
        yield from it

result = list(chain([1, 2], "ab", (10, 20)))
print(result)  # [1, 2, 'a', 'b', 10, 20]

注意点

yield from は、サブジェネレータが StopIteration を送出するまで、すべての値を委譲します。途中で制御を戻すことはできないため、条件付きで中断したい場合は通常の for ループと yield を組み合わせてください。