Pythonのyield from(サブジェネレータへの委譲編)

はじめに

なんとなく使ってたPythonのyieldについて調べるシリーズ第3弾です。
今回はyield from構文をみていきます。この記事の内容は、Python3.5.2/Windows10で試しています。

yield fromの前に

ジェネレータ関数の知識がないと、なかなか理解が進まないので、まずはジェネレータ関数を抑えておくことをお勧めします。以下のスライドの16p~20pの説明が分かりやすいです。

yield fromの概要

yield from構文はPython3.3で追加された構文です。以下はリリースハイライトの抜粋ですが、

これは ジェネレータ に、その操作の一部をほかのジェネレータに委譲するための式です。

単純なイテレータに対して、 yield from iterable は本質的には for item in iterable: yield item への単なる速記法です。

ということなので、yield from構文がないPython3.3以前でも同等のことはできます。

ほかのジェネレータに委譲 ?

とは具体的にどういうことなのでしょうか?ハイライトをもう少し読み進めると

この変更を推し進める一番本質にあったものは、… 大きな関数を複数のサブ関数に分割するのと同じくらいに、ジェネレータを簡単に複数のサブジェネレータに分割出来るようにすることでした

ということなので
大きい関数を小さい関数に分割するのと同じように、ジェネレータも小さく分割できるようにする。
ことがこの構文のメインの役割のようです。

以上を踏まえて、早速サンプルを書いてみます。

yield fromを使った単純なサンプル

まず、ただのジェネレータ関数を2つ(0,1,2,3作るのと3,2,1を作るのの2つのジェネレータ関数)を書きます。

def gen_1():
    for i in range(4):
        yield '<< %s' % i

def gen_2():
    for i in range(3, 0, -1):
        yield '<< %s' % i

これら2つのジェネレータ関数を使うジェネレータ関数をgen_wrapperとし(後述します)、以下のmainで動かした場合、

if __name__ == '__main__':
    gen_func = gen_wrapper(gen_1(), gen_2())
    for i in gen_func:
        print(i)

以下の出力となるようにジェネレータ関数gen_wrapperを作っていきます。

...<< 0
...<< 1
...<< 2
...<< 3
...<< 3
...<< 2
...<< 1

yield fromを使わないgen_wrapper

def gen_wapper(gen_func1, gen_func2):
    for i in gen_func1:
        yield i
    for i in gen_func2:
        yield i

yield fromを使ったgen_wrapper

def gen_wrapper(gen_func1, gen_func2):
    yield from gen_func1
    yield from gen_func2

どちらのgen_wrapperも機能的には同等なジェネレータ関数ですが、yield from構文を利用したほうがより関数が実現したい意図が読み取りやすくなっていますね。

まとめ

yield from 構文を利用することで、ジェネレータ関数を小さい単位に分割しやすくなり、より可読性の高いコードを書くことができます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA