オブジェクトの要素を一つずつ取り出す

反復可能なオブジェクト

for i in x:
    print i
 
l = [n for n in x if n % 2]

x は要素を一つずつ取り出せる反復可能なオブジェクトです。反復可能なオブジェクトは __iter__() でイテレータオブジェクトを返す必要があります。

class Test:
    def __iter__(self):
        return self.__dict__.iteritems()

__dict__ はオブジェクトの属性の辞書、iteritem() はキーと値のタプルを返すイテレータオブジェクトを返します。

t = Test()
t.s = 1
t.a = 2
t.y = 3
for kv in t:
    print "%s => %s" % kv
a => 2
y => 3
s => 1

イテレータオブジェクト

イテレータオブジェクトは反復可能なオブジェクトですが、__iter__() で自分自身を返し、次の要素を返す next() を持つ必要があります。next() は最後の要素を返した後は、何度呼び出しても StopIteration 例外を投げなければなりません。そうしないと無限ループになってしまいます。


では、0〜n までの素数を返すイテレータオブジェクトを作ってみます。

import math

class Prime:
    def __init__(self, n):
        self.nums = range(2, n+1)
        self.root_n = math.sqrt(n)

    def __iter__(self):
        return self

    def next(self):
        if len(self.nums):
            # 1回目のpは2、つまり素数
            p = self.nums.pop(0)
            if self.root_n > p:
                # 素数pで割り切れる要素を全て取り除く
                # これでself.numsの先頭は次の素数になる
                self.nums = [n for n in self.nums if n % p]
            return p
        raise StopIteration

まず素数で始まる数値のリストを用意します。そして、next() が呼ばれるたびにリストの先頭の要素を返し、。同時にリストからその要素で割り切れる数を取り除きます。こうするとリストの先頭の要素は必ず素数になります。また、n以下の素数でない数は必ず√n で割り切れるので、素数で要素を取り除くは、先頭の要素が√n 以下の間だけです。

p = Prime(100)
print [n for n in p]
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 
 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]