yield がどうもピンとこない
python の yield は非常に便利ですが、イマイチ判りにくかったりします。内部的な仕組み、たとえば iter とか next を理解しとけばいいのですが、それはそれで面倒です。
そこで内部の仕組みはひとまず横において、とりあえず yield の動きを理解することを目指します。そして、そういう目的なら return と比較するのが簡単です。
return との比較
return と yeild を比較してみます。
まず return ですが、戻り値のある return の動作は
- 関数の処理を 終了 し
- 値を返す
です。
一方、yeild は
- 関数の処理を 一旦停止 し
- 値を返す
という動きをします。
一旦停止なので、yeild の処理は再開されます。
図にすると


yeild の場所で一旦停止し、再開時は次の行からスタートします(再開のきっかけは後述)。
なんのために ?
でわ、yield があると何が便利なのでしょうか。
単純な例ですが、たとえば 1GB の巨大なテキストファイルがあるとします。そして、この巨大なファイルを読み込み、データを渡してくれる関数を作るとします。
これを普通にやろうとすると、受け渡し用のメモリが 1GB という巨大なサイズになってしまいます。
ところが yield を使えば、少量、たとえば 1 行づつデータを読み込み、その都度 yield すればいいので、メモリの使用量はほんの僅かで済んでしまいます。

使ってみる
ループで
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
def func(): a = 10 b = 20 c = a + b yield c # ここで一旦停止 a = 100 b = 200 c = a + b yield c # ここで一旦停止 a = 1000 b = 2000 c = a + b yield c # ここで一旦停止 for x in func(): print (x) #---- 結果 ----- # 30 # 300 # 3000 #--------------- |
yield がなくなるまで for 文が回りつづけます。
for 文の裏側で 「処理(yield まで) ⇒ 一旦停止 ⇒ 再開(yield まで) ⇒ 一旦停止」 が繰り返され、その都度、yield の返す値が x に代入されます。
ループ以外で
for 文のような繰り返し処理を使わない場合は、内部の仕組みを自分で意識する必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 |
gen = func() # ジェネレータ print (gen) print (gen.next()) # 1 回目 ※ Python3系では、__next__() print (gen.next()) # 2 回目 print (gen.next()) # 3 回目 #---- 結果 ----- # <generator object func at 0x00000000145D1240> # 30 # 300 # 3000 #------------- |
yield を含む関数を呼び出すと generator(ジェネレータ)というものが返ってきます。そのジェネレータの next() を呼ぶと、yield までの処理を実行し、値を返してくれます。next() をもう一度呼ぶと、yield の次の行から再開され、次の yield まで処理を実行し、値を返してくれます。
yield の個数以上に next() を呼ぶと StopIteration 例外が raise されます。
zz says:
最高にわかりやすい感謝!
管理人 says:
zzさん、コメント、どうもありがとうございます。
励みになります。
匿名 says:
わかりやすい
管理人 says:
ありがとうございます
匿名 says:
コルーチンと似たような使い方ができるということか.勉強になりました.
管理人 says:
コルーチンという言葉を初めて知りました。勉強になりましたw
python嫌いおじさん says:
パーフェクトPythonという本をパラパラ立ち読みしててyeildが出てきて、説明が雑だったので見に来ました! 図や簡単なコードで説明されてて、とてもわかりやすいです! 感謝…
管理人 says:
python嫌いおじさん、こんにちわ。
コメント、どうもありがとうございます。
python嫌いなどと言わず、ぜひ Anaconda あたりから。
匿名 says:
Python3系では、__next__()ですね!
管理人 says:
匿名さん、ありがとうございます。
そのままソース・コメントに使わせてもらいました。
Wsan says:
とても分かり易かったです!
管理人 says:
Wsan さん、こんにちわ
コメントどうも有難うございます。
motoko says:
わかりやすい記事をありがとうございます。初心者ですがreturnとyeildの違いがなんとなく理解できました。
管理人 says:
motokoさん
コメントどうも有難うございます。
ishida says:
なるほど!yieldは「いったん停止して戻る」動きなんですね。わかりやすい解説をありがとうございました。
管理人 says:
ishida さん
コメントどうも有難うございます。
hnkyi says:
あれこれ見たんですが、ここが一番分かりやすかったです。ありがとうございます!
管理人 says:
hnkyi さん
コメントどうも有難うございます。
Scrapyがんばっぞ! says:
Scrapyをやりながら、yieldで色々と迷っていました!
こちらの解説でようやく腑に落ちました。
ありがとうございます。
管理人 says:
コメントどうも有難うございます。
ぜひ、がんばってください。
suuuu says:
ジェネレーターに関して調べていて、ここに行き着きました。
とても分かりやすかったです。
参考にさせていただきました。
外部リンクで紹介させていただきます。
管理人 says:
suuuu さん、コメントどうも有難うございます。
リンク、よろしくお願いします。
かめさん says:
すごくわかりやすいです!
ありがとう
管理人 says:
かめさん
コメント有難うございます。
匿名 says:
やっとyieldの意味が分かりました
こうして徹底的に「何を意図して使うか」の観点にしてもらうと、入門書の四角い文章よりもスッと概念が頭に入ってくるので助かります、ありがとうございました
管理人 says:
コメント、ありがとうございます
匿名 says:
解説わかり易かったです。
これからも頑張ってください。
ありがとうございます。
管理人 says:
コメント、どうも有難うございます
匿名 says:
わかりやすい解説、ありがとうございます。一つコメントです。yieldを含む関数がgenerator関数で、generatorの返り値は(generator) iterater オブジェクトでは?
匿名 says:
iterater → iterator
通りすがり says:
いやいや、ループでyieldを使う例が、分かりにくいでしょう。
yield使うだけで、何の調整処理もしてないのに、勝手にプログラムが
一つの伝送データを小分けにして、処理を繰り返してくれる様子をきっちり
伝えなくては本当に理解しているとは思えません。
記事を完成させた為だけに手を抜かないで下さい。
yield使うだけで、何の調整処理もしてないのに、勝手にプログラムが 一つの伝送データを小分けにして、処理を繰り返してくれる様子をきっちり 伝えなくては本当に理解しているとは思えません。⇦読みづらい! says:
否定するなら、参照先の一つでも貼るのが道理では?
勉強中 says:
とても分かりやすかったです。解説ありがとうございます。
ジェネレータ難民 says:
使い方が良く理解できました。
ありがとうございます。
yanus says:
very easy to understand!
Yusuke says:
有料級のコンテンツですね!ありがとうございます。
匿名 says:
6箇所もyeildってスペルミスしてて本当に大丈夫か不安になった
tatta says:
とってもわかりやすかった!そして「はー?つか・・う・・価値・・・・・?」と疑問だったのが解消できました!
メモリ節約を目指して使おう!
ありがとうございました!
匿名 says:
どの説明よりもわかりやすいです!
匿名 says:
ちなみに今は__next__()メソッドになってますね。
匿名 says:
イラストで表現していただけて,非常に判りやすかったです.
参考にします.