pythonのyieldとgeneratorに関して個人的メモ。まだ作りかけ。

目次

「yield」と「generator」について

yield

yield は英語で「生み出す」「生む」「起こす」といった意味の単語とのことで、 Python におけるyieldは、 コードを構成する構成要素(「キーワード」)のひとつで、 yield 式を作るためのもの。

「yield式」は、関数(あるいはメソッド)の中にのみ記述することができるもので、yieldが記述された関数をジェネレータ関数化する。

ジェネレータ関数

「ジェネレータ関数」とは、関数の中でも少し特殊な関数で、通常の関数が 1 つの値をreturnで返すのに対して、ジェネレータ関数はジェネレータオブジェクトを返す。

「ジェネレータオブジェクト」とは、ジェネレータによって生成されたイテレータオブジェクトのこと。

イテレータオブジェクト

「イテレータオブジェクト」とは、forループや組み込みのnext()関数に渡せば値を 1 つずつ返してくれるオブジェクトのこと(厳密には、 iter() ```` next() の 2 つのメソッドを持っていて、 iter() メソッドの戻り値が self (自分自身)のオブジェクト)。

forループすれば値を順番に取り出せるのでリストと同じように感じるが、リストは順番が決められたデータを表す構造であってイテレータではない。forループで使えるものはイテレーターとは言わず「イテラブル(Iterable)」という。

要はループしたら使い切られるものがイテレーターで、for in ...に渡せるものをイテラブルと呼ぶ。

具体例

1個ずつ値を返してほしい関数として使いたいならyieldを使う

def order_gene(p):
    filesPath = sorted(p.glob("**/*.*"))
    for f in filesPath:
       yield os.path.abspath(f) 

1つのgeneratオブジェクトが欲しいならタプル「()」を使えばOK

def order_gene_object(p):
    filesPath = sorted(p.glob("**/*.*"))
    return (os.path.abspath(f) for f in filesPath)

普通にリスト型で欲しい時なら「[]」を使う

def order_list(p):
    filesPath = sorted(p.glob("**/*.*"))
    return [os.path.abspath(f) for f in filesPath]

これをreturnじゃなくyieldにしてしまうとlist1個だけ持ったgenerator型が帰って来てしまう forで回すと最初listを返す。もう一度forしないとstrにならない。 つまりやってることがlist[0][0]になるので無駄

def Hoge(p):
    filesPath = sorted(p.glob("**/*.*"))
    yield [os.path.abspath(f) for f in filesPath]

どんな時にリストとジェネレータを使い分ければいいか

リストは何度も同じ数列を使いたい場合。ジェネレータ繰り返し使うのに向いてない。 つまりリストとして値を保持し続けなくていい場合や、リストの容量が多いまたは最大どれくらいになるのか把握できない場合はジェネレータにして対応したほうが良い。