AM 08:00
さーて、頼まれてたクローラー、サクサクつーくろ
( ・ω・)ノ オー
PM 01:00
ふーむ、あらかたscrapyで用意することできたわ
単純なクローラーだったらサクッて作れるね
( ^ω^)おっおっおつ
PM 01:15
でも、ちっと連続でクローリングするbotみたいなもの
作りたいから、もうちょいやってみよう。
そうだ、別関数を用意して回すのはどうかな?
( ・ω・)?
PM 01:30
英語・・・。んん?この「yield」ってなんやねん。
ふむふむ、ジェネレータがうんちゃらかんちゃら?
( ・ω・)?
まぁ、使わなければいいでしょ。
PM 08:00
( ・ω・)
( ;ω;)ブワッ
できなかったよ!!
なんかしらんけど、英語圏のQ&Aってだいたいが「yield」
使っているから、回避できへん!!
クッソ、こうなったら徹底的に理解してやる・・・・!!
1. 「yield」とは?
「yield、と言ったら、ジェネレータ」と考えたらダメらしい。
ちゃんとよく読まないでそう思っていたのは、私です。
すみません。
では?なんでしょう?
Pythonでは、関数定義の中にyield文があると、その関数定義は通常の関数を定義するのではなく、一種のコルーチンの記述のようになる。yield文を含む関数は、イテレータと同じインタフェースを持つ呼び出し可能オブジェクトを返す関数になる。ジェネレータの語は、「yield文を含む関数定義により定義された関数」と、それが返す「イテレータと同じインタフェースを持つ呼び出し可能オブジェクト」を、はっきりと区別せずに使われているが、ここでは、前者をジェネレータ、後者をイテレータと呼ぶ。
このイテレータは、ジェネレータの定義中の各yield文の所まで実行した状態を保存するスタックフレームを保持するオブジェクトであると考えることができる。イテレータのnext()が呼び出されると、Pythonは保存されたフレームを復帰し、次のyield文に到達するまで実行する。yield文の実行によりフレームは再び保存され、yieldの引数の値がnext()の呼び出し元に返される。
と、いうことらしい。
なるほど!わからん!!
( `・ω・)クワッ!
もう少し調べたところ
通常、関数は「return」を利用して、値を返却しますが
「yield」はそうではなく、処理を途中で止めて、保存しておく・・・?
ってことができるとか。
全然理解してなにので、例で復習します。
def fruits(): yield 'apple' yield 'orange' yield 'meron' for i in fruits(): print i
上のスクリプトを実行すると
apple orange meron
と、なる。
もうちょっと、わかりやすい内訳
def fruits(): yield 'apple' yield 'orange' yield 'meron' # 'apple'がでてくる print fruits().next() # 'orange'がでてくる print fruits().next() # 'meron'がでてくる print fruits().next()
むずかしい。。。
まぁ、上をみたらわかると思うけど
yieldを使うと、その時点で関数(とは違うけど)の処理を中断します。
中断された処理は、next()で再開するようになるとか。
また、next()じゃなくとも、forループ文でも再開できる。
next()自体と、それ以外のメソッドを使って、繰り返し処理を行っている。
人によって、理解のイメージは違うが
ティッシュ箱だったり、冷凍野菜みたいなイメージを持っている人もいました。
冷凍野菜がわかりやすいかな?
一旦は冷凍保存して、使いたいときにチーン!して、取り出すみたいな。
※いや、わかりにくいか?
2. 何が便利なの?returnでいいじゃん。
そうだね。全く便利な感じがしないよね。
だけども、圧倒的理由が存在します。
隊長は今のところ、無駄を無くすってイメージで理解しています。
たとーえば
# yield def yield_fruits(): yield 'apple' yield 'orange' yield 'meron' # normal function def normal_fruits(): list = ['apple', 'orange', 'meron'] return list
上の関数(ジェネレータ?)と、下の関数はやること違えど
帰ってくる値は一緒です。
では、何が便利かというと
メモリの効率化が上げられます。
yieldのほうが効率が良い場合があるんです。
ここで、利用したい値が「apple」だけだとしましょう。
yieldの場合は、「apple」は1回めに帰ってくるので
それ移行はループさせなくてもOKです。
しかし、normal_functionの場合は、3つのlistが返却されてきます。
これが例えば、数十万、数百万のlistであればどうでしょう?
必要なもの以外返却されてては、メモリの消費がバカになりません。
こういった時にめっちゃ便利なんだとか。
3. イテレータ?ジェネレータ?
これもわかりませんでした。
yield触っていると、この2つの説明とよく出くわしました。
ついで、理解しようと思いました。
イテレータ?(´・ω・)?
イテレータ(英語: Iterator)とは、プログラミング言語において配列やそれに類似するデータ構造の各要素に対する繰返し処理の抽象化である。実際のプログラミング言語では、オブジェクトまたは文法などとして現れる。反復するためのものの意味で反復子(はんぷくし)と訳される。繰返子(くりかえし)という妙訳もある。
んーと、繰り返し処理のこと!
んで、iter()って組み込み使って、next()で次の要素を取得するってこと。
for文も実は、next()使ってました!
って感じかな(´・ω・)アッテルカナ?
でも、for文はイテレータではない。
for文はイテレータを使う場合は使うが、そうでない場合は
コンテナオブジェクト(__getitem__())を使うらしいので
そう括ることができないんだとさ。
なんとなく理解した。
ジェネレータ???(;´・ω・)???
これじゃないらしい。(あたりめーよ)
じゃあ!なんなの!!?
ジェネレータは、プログラムにおいて、数列の各要素の値などを次々と生成(ジェネレート)し他の手続きに渡す、という機能を持っている手続きである。値を渡す方法としては、コールバックのようにして他の手続きを呼ぶものもあれば、呼び出される度に次々と異なる値を返す関数であることもある。
えと、、、えっと・・・?
よくわからない。。。
とりあえず、キーワードがあるらしい。
まぁ、これなら動き方はだいたい理解できるかも。。。
そうすると、yieldはジェネレータってことになりますよね?
ではない・・・だと・・・!?(Wiki参照)
なんでや!!?
めっちゃ同じこといってますやん!!?
とりあえず、この点に関してはシカトします。よくわからん。
今のところは、「yield使うと、ジェネレータ使えます」ってことで。
うーむ。仕様についてはいまいちピンときていないけど
まぁ、使い方はわかったかな。
以上
それじゃ、頑張ろ( ・ω・)つ