Dive into Python 3に戻って、chapter 7と8からイテレータの話。
オブジェクトobjをlistやtupleのようにforでぶん回せる(iterableである)ようにするには、
- objが__iter__()メソッドを持っている
- obj.__iter__()が返すオブジェクトが__next__()メソッドを持っている
では、generatorはどうしてiterableだったのか。別に特別扱いされていたわけでも何でもなく、generatorも上の条件を満たしていたのだ。
>>> def gen():generatorは実は、自分自身を返す__iter__()と、yieldした値を返す__next__()を持っていたのだ。なおgeneratorは、StopIterationを明示的にを投げなくてもreturnするときに投げてくれるようだ。普通の関数と違って、yieldとreturnを区別できるからだろうね。
... yield 0
...
>>> g = gen()
>>> g.__iter__
<method-wrapper '__iter__' of generator object at 0x10a88cf78>
>>> g == g.__iter__()
True
>>> g.__next__
<method-wrapper '__next__' of generator object at 0x10a88cf78>
>>> g.__next__()
0
>>> g.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
ちなみに、普通のlistやtupleも例外ではなく、__next__()を持っているオブジェクトを返す__iter__()を持っている。
この緩い条件を満たしたiterableなオブジェクトから、別のiterableなオブジェクトを生成するitertoolsというライブラリが、Pythonには標準で付いている。chapter 8ではitertoolsの中からいくつかの関数を使って見せているけど、詳しくはhelp()で調べてみれば良いだろう(きちんとdocstringが用意されているって、地味に凄いことだよなぁ)。…と、これだけで片付けるのはあんまりなので、iteratorのありがたみを3行で表す(パッと見2行だけど、printの次の空行を含めて3行)。
for p in itertools.permutations(range(10000)):順列を列挙するコードを自前で書かずに済むのはありがたいけど、それ以前にこのコードが動くこと自体がありがたい(実行に何年かかるか知らないが)。ちょっと考えれば分かるように、10000要素の整数リストを10000!個持つリストを作ってからループを回そうなんて思ったら、どう考えてもメモリが足りない。それでもこのコードが延々と順列を表示し続けてくれるのは、いきなり10000!通り全てを生成しようなんて馬鹿げたことをせず、next()の度に1つずつ列挙してくれているからだろう。ここらへんのケアをおまかせできるのは美味しい。
print(p)
0 件のコメント:
コメントを投稿