イテレータを実装してみた

日曜日, 1月 16th, 2011 by

Pythonのforループは、Iterableなオブジェクトに対して実行します。

1
2
for 一時変数 in イテレートできるオブジェクト:
  ループ内の処理

Iterableかどうかは、どのように判断されるのでしょうか?Javaでは、Iterableインターフェースを実装しているかどうかで判定されますが、Pythonにはこういった仕組みはありません。オブジェクトが繰り返し処理(Iterate)に対応したメソッドをもっていればそれでよいのです。こういう仕組みをダック・タイピングといいます。

要するに、Pythonで、Iterableにするためには以下の2つのメソッドが実装されていればよいのです。

  • next()
    次の要素を返す。次の要素が無ければStopIteration例外を投げる。
  • __iter__()
    自分自身を返す

カウントダウン反復子

試しに、カウントダウンを行うイテレータを定義してみた。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> class CountDownIter(object):
...   def __init__(self, start):
...     self.i = start
...   def __iter__(self):
...     return self
...   def next(self):
...     if self.i <= 0:
...       raise StopIteration
...     self.i = self.i - 1
...     return self.i
...
>>> for i in CountDownIter(10):
...   i
...
9
8
7
6
5
4
3
2
1
0

これぐらいだと、イテレータを自作するメリットはありませんね。以下のように記述したほうが分かりやすいし楽です。

1
2
3
4
5
6
7
8
9
10
11
12
>>> for i in reversed(range(10)):
...   i
...
9
()
0
>>> for i in range(10)[::-1]:
...   i
...
9
()
0

フィボナッチ数列イテレータ

指定した数までのフィボナッチ数列を返すイテレータを作ってみた。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
>>> class FibIter(object):
...   def __init__(self, limit):
...     self.i, self.j = 0, 1
...     self.limit = limit
...   def __iter__(self):
...     return self
...   def next(self):
...     self.i, self.j = self.j, self.i + self.j
...     if self.i > self.limit:
...       raise StopIteration
...     return self.i
...
>>>
>>> for i in FibIter(10):
...   i
...
1
1
2
3
5
8
>>> [i for i in FibIter(20)]
[1, 1, 2, 3, 5, 8, 13]
>>> list(FibIter(20))
[1, 1, 2, 3, 5, 8, 13]
>>> FibIter(20)
<__main__.FibIter object at 0x0000000002291860>
>>> print FibIter(20)
<__main__.FibIter object at 0x0000000002291860>

forループの対象として指定できるだけでなく、リスト内包表記で使ったり、list関数を使ってリストに変換したりできる。ただし、実体はIteratorなので、printで出力した場合はリストとして出力されない。

まとめ

__iter__メソッドと、nextメソッドさえ実装すればイテレータになり、forループやリスト内包表記の対象として指定できる。

Facebook comments:

comments

Leave a Reply


Get Adobe Flash player
single