2013年11月16日土曜日

[Python] リスト内包表記とジェネレータ式

リスト内包表記とジェネレータ式。
リストとタプルみたいに見た目はよく似ているけど、実はぜんぜん違う。

リスト内包表記
>>> power_list = [n**2 for n in range(5)]
>>> power_list
[0, 1, 4, 9, 16]
>>> for n in power_list:
...     print n
... 
0
1
4
9
16

ジェネレータ式
>>> power_gen = (n**2 for n in range(5))
>>> power_gen
<generator object <genexpr> at 0x103547cd0>
>>> for n in power_gen:
...     print n
... 
0
1
4
9
16

イテレータとして使う限りは全く同じ挙動に見えるけど、
リスト内包表記は、作成時にリストの中身を全て計算して、リストを作成しているのに対して、
ジェネレータ式は、呼び出された際に順に式を計算している。
リストを丸ごと記憶しておく必要がないので、メモリの使用を抑えられる。

そもそもジェネレータとは、こんな感じ。
>>> def power_func():
...     n = 0
...     while True:
...         yield n**2
...         n += 1
...
>>> fun = power_func()
>>> fun.next()
0
>>> fun.next()
1
>>> fun.next()
4
>>> fun.next()
9
>>> fun.next()
16

このyield文を上手く説明できないんだけど、僕の認識だと、
値を返して、ここまで計算したよというのを覚えておく式、という感じ。
next()が呼び出されると、yield文の続きから計算を行う。

だから、関数power_func()のように終わりのないループを書くと、
>>> for n in power_func():
...     print n
... 
0
1
4
9
16
25
36
49
...
のように、延々とイテレーションが回ってしまう。
けど、逆に言えば、何か処理を行った際の値でループを抜けるように書けば良い話。

うーむ、pythonicな書き方に慣れないとな。

0 件のコメント:

コメントを投稿

Related Posts Plugin for WordPress, Blogger...