バリケンのPython日記 RSSフィード

2008-10-16

[] イテレータとジェネレータ(1)  イテレータとジェネレータ(1) - バリケンのPython日記 を含むブックマーク はてなブックマーク -  イテレータとジェネレータ(1) - バリケンのPython日記  イテレータとジェネレータ(1) - バリケンのPython日記 のブックマークコメント

Pythonの「イテレータ」と「ジェネレータ」がよく分かってないから、勉強してみるよ。

と言っても、いきなり「イテレータ」や「ジェネレータ」の説明を読んでも何だか分からないから、まずはPythonシェルで色々と動作させて実験してみるよ。

以前リスト内包表記について勉強したけど、たとえば3の倍数が順に10個入ったリストが欲しいときは、

>>> [x * 3 for x in range(10)]
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
>>>

のように書いたよね。この「[x * 3 for x in range(10)]」の角カッコを丸カッコに変えるとどうなるかな?

>>> (x * 3 for x in range(10))
<generator object at 0x00C3AD78>
>>>

おや、「ジェネレータオブジェクト」というものが返ってきているね。

このオブジェクトが一体何なのかを調べてみよう!まずは変数に代入して、dir関数でどんなメソッドや属性を持っているかを調べてみるね。

>>> g = (x * 3 for x in range(10))
>>> dir(g)
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__', '__init_
_', '__iter__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr
__', '__str__', 'close', 'gi_frame', 'gi_running', 'next', 'send', 'throw']
>>>

じゃあ、ちょっと天下り的だけど、このオブジェクトに対して「next()」メソッドを何度か呼んでみよう!

>>> g.next()
0
>>> g.next()
3
>>> g.next()
6
>>>

どうやら、このジェネレータオブジェクトはnext()メソッドを呼ぶたびに3の倍数を順に返すみたいだね。

リスト内包表記の場合はメモリを確保して一気に計算してリストを返すけど、ジェネレータオブジェクトの場合はnext()メソッドが呼ばれてから計算しているから、計算資源とメモリにやさしいみたいだよ。

あと、要素がなくなってからnext()メソッドを呼ぶとどうなるかも試してみよう!さっきの続きから試してみると、

>>> g.next()
9
>>> g.next()
12
>>> g.next()
15
>>> g.next()
18
>>> g.next()
21
>>> g.next()
24
>>> g.next()
27
>>> g.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

となって、次の要素がもうないジェネレータオブジェクトに対してnext()メソッドを実行すると、StopIterationという例外が発生するみたいだよ。