Classes

また昨日の続きです。

Generators

イテレータ関連の話題が続きます。yield文です。JavaではThread.yield()として利用するのですが、全然意味が違います。C#のyield文と同じです。ループなどの中で利用すると処理の途中でreturnすることが出来、かつ処理をそこから再開させることが出来るのです。まさしくイテレータの動きですよね。yield文を使ったイテレータのことをGeneratorといいます。

>>> def reverse(data):
...     for index in range(len(data) - 1, -1, -1):
...             yield data[index]
... 
>>> for char in reverse('golf'):
...     print char
... 
f
l
o
g


forループの途中で抜けても再開出来ているのが分かります。でも少し疑問なのは昨日イテレータを作った時は、__iter__メソッドとnextメソッドを定義しました。またnextメソッドの中では終端に達したらStopIteration (例外) を送信していました。でも↑の例ではそんなことはしていません。これはどういうことでしょうか?チュートリアルには

the __iter__() and next() methods are created automatically.
...
when generators terminate, they automatically raise StopIteration.


とあります。つまりyield文を使うとこの辺りのことを全部自動でやってくれるという訳です。う〜ん、私はこういうのあんまり好きじゃないです。

Generator Expressions

そんな感じのGeneratorですが、実は今までも結構使っているのです。たとえばrange関数やzip関数がそうです。

>>> sum(i * i for i in range(10))    # 0〜9までを足す
285
>>> xvec = [10, 20, 30]
>>> yvec = [ 7,  5,  3]
>>> sum(x * y for x, y in zip(xvec, yvec))  # 10*7 + 20*5 + 30*3
260


なお、以前勉強したリスト内表現、つまりGeneratorではなく、リストを直接記述する表現より、こちらのGeneratorの方がメモリ効率がよいそうです。


※リスト内表現

>>> sum(i * i for i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
285