複数の要素からなるオブジェクトから各要素を順に取り出すためのインタフェース

イテレータ

イテレータは、複数個の要素を保持しているオブジェクトから、要素を一つずつ順に取り出すためのインタフェースである。複数個の要素を保持している型として、リスト、ディクショナリ、タプル、集合などがある。これらのオブジェクト(例えば、リスト)に対して、for 構文を適用すると、リスト中の各要素を順に取り出すことができる。これは、リストがイテレータとしての機能が実装されているために、for 構文による要素の取り出しが可能になるからである。

for 構文を利用せずに、イテレータの基本的な制御方法でリストの要素を取り出す例を次に示す。はじめに、リストを iter 関数に代入し、リストをイテレータとして制御できるような形に変換する。次に、next 関数を使って、リスト中の最初の要素を取り出す。その後、next をもう一度使って、2 番目の要素を取り出す。このように、iter 関数でリストをイテレータに変換すると、next 関数を必要な回数だけ実行して、要素を 1 つずつ順に取り出すことができるようになる。そして、最後の要素に達したあとに、next 関数を実行すると、StopIteration 例外が発生する。

a = [1, 2, 3, 4, 5]
a_iter = iter(a)

next(a_iter)
# 1

next(a_iter)
# 2

next(a_iter)
# 3

next(a_iter)
# 4

next(a_iter)
# 5

next(a_iter)
# StopIteration

リストに対して for 構文を適用すると、リストの中から要素を 1 つずつ順に取り出すことができる。これは、for 構文を使用したときに、自動的に iter 関数が呼び出されて、その後、next 関数を繰り返して実行して、StopIteration となったら for 構文から抜ける、という制御が書かれていると考えても良い。

イテレータを自作することができる。イテレータをクラスとして定義し、クラスの中で __iter__ および __next__ メソッドを定義する。iter 関数を使用したときに __iter__ が実行され、next 関数を使用したときに __next__ が実行される。次は、5 つの値を受け取り、それらをイテレータとして使えるような形で変換している例である。

class Arr:
    def __init__(self, a, b, c, d, e):
        self.x = [a, b, c, d, e]
        self.i = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.i == len(self.x):
            raise StopIteration()
        
        ret = self.x[self.i]
        self.i = self.i + 1
        return ret

a = Arr(1, 2, 3, 4, 5)
for i in a:
    print(i)

# 1
# 2
# 3
# 4
# 5

もう一つの例を示す。与えられたリストの中から、奇数番目の要素を取り出すイテレータの例である。

class EvenList:
    def __init__(self, arr):
        self.x = arr
        self.i = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        current_i = self.i
        
        # i に 1 を足して、次の奇数番目要素のインデックスを取得する
        self.i = self.i + 2
        
        if current_i == len(self.x):
            raise StopIteration()
    
        return self.x[current_i]


a = EvenList([1, 2, 3, 4, 5, 6, 7, 8])

for i in a:
    print(i)

# 1
# 3
# 5
# 7