この記事では、Pythonのイテラブルオブジェクトとイテレータについて解説します。よくリストなどやfor文あたりで見かけますが、一体どのようなものなのでしょうか?それでは、一緒に確認していきましょう!
イテラブルオブジェクトとは?

イテラブルオブジェクトとは、簡単に言うと「反復可能なオブジェクト」のことを言います。以下のような型があります。
- シーケンス型(リスト、タプル、range)
- マッピング型(辞書)
- テキストシーケンス型(文字列)
- バイナリシーケンス型(bytes、bytearray、memoryview)
繰り返し処理の仕組み
例としてfor文が繰り返し処理する流れを見てみましょう!
- for文が指定されたイテラブルオブジェクトに対して__iter__メソッドを呼ぶ
- __iter__メソッドは__next__メソッドが定義されているオブジェクトを返す
- 返されたオブジェクトに対して__next__メソッドを繰り返し呼び出す
- __next__メソッドがStopIteration例外を送出するとループが終了する
このように繰り返し処理をしています。つまり、イテラブルオブジェクトとは__iter__メソッドが定義されているオブジェクトのことです。
イテレータオブジェクトを生成
__iter__メソッドを呼び出すとイテレータオブジェクトが返される。
l = [1, 2, 3]
# iter関数は__iter__メソッド呼び出す
print(iter(l))
# 直接呼び出す
print(l.__iter__())
実行結果
<list_iterator object at 0x10d8402d0>
<list_iterator object at 0x10d84d3d0>
for文で繰り返し処理する際、イテラブルオブジェクトに対して__iter__メソッドが呼び出されています。ということは、イテラブルオブジェクトはイテレータオブジェクトを生成して繰り返し処理をしていることがわかります。
イテレータとは?

イテレータとは、イテレータオブジェクトという型の1つで「__next__メソッドによって要素を取り出せるオブジェクト」です。
__iter__メソッドで返した__next__メソッドが定義してあるオブジェクトを元に、イテレータオブジェクトが生成されます。
イテレータオブジェクトを使ってみる
試しにイテレータオブジェクトから要素を取得してみましょう。
for文で使ってみる
リストと同じように処理されました。
l = [1, 2, 3]
iterator = iter(l)
for i in iterator:
print(i)
実行結果
1
2
3
次の要素
next関数を使って次の要素を取得できます。
次の要素がない場合は、StopIteration例外が送出されます。
l = [1, 2, 3]
iterator = iter(l)
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
実行結果
1
2
3
Traceback (most recent call last):
print(next(iterator))
StopIteration
イテレータプロトコル

__iter__メソッドと__next__メソッドを合わせてイテレータプロトコルと呼ばれます。イテレータプロトコルを実装することで、ユーザー定義クラスにイテラブルオブジェクトとしての振る舞いを追加できます。
__iter__メソッド
__next__メソッドが定義されているオブジェクトを返す。
def __iter__(self):
return __next__メソッドが定義されているオブジェクト
自身に__next__メソッドが定義されているならselfを返すことができる。
def __iter__(self):
return self
__next__メソッド
呼び出されるたびに要素を返します。
返す要素がなければStopIteration例外を送出します。
def __next__(self):
# 要素を返す
# 返す要素がなければStopIteration例外を送出
イテレータプロトコルを実装してみる
以下のコードは、コンストラクタで受け取った不特定多数の要素をfor文でループ処理できるようにしました。
class MyIterator:
def __init__(self, *args):
self.__args = args
self.__index = 0
# __next__メソッドを定義しているオブジェクトを返す
def __iter__(self):
print('__iter__')
return self
# 呼び出されるたびに新しい要素を返す
# 最後にStopIterationを返す
def __next__(self):
print('__next__')
if self.__index == len(self.__args):
print('StopIteration')
self.__index = 0
raise StopIteration()
arg = self.__args[self.__index]
self.__index += 1
return arg
myiter = MyIterator(1, 2, 3)
for arg in myiter:
print(arg)
実行結果
__iter__
__next__
1
__next__
2
__next__
3
__next__
StopIteration
簡単にメソッドの内容を見てみましょう!
__iter__メソッド
__iter__メソッドではselfを返しました。
def __iter__(self):
print('__iter__')
return self
__next__メソッド
呼び出されるたびに__index変数に1を足して、毎回新たな要素を返します。__index変数と__args変数の長さが同じになったら返す要素がないのでStopIteration例外を送出します。
def __next__(self):
print('__next__')
if self.__index == len(self.__args):
print('StopIteration')
self.__index = 0
raise StopIteration()
arg = self.__args[self.__index]
self.__index += 1
return arg
イテレータプロトコルをバラバラに実装する
先ほどのMyIterationクラスをバラバラにして実装してみます。
# __iter__メソッドを定義したクラス
class MyIter:
def __init__(self, *args):
self.__args = args
def __iter__(self):
print('__iter__')
# MyNextを呼び出す
return MyNext(*self.__args)
# __next__メソッドを定義したクラス
class MyNext:
def __init__(self, *args):
self.__args = args
self.__index = 0
def __next__(self):
print('__next__')
if self.__index == len(self.__args):
print('StopIteration')
self.__index = 0
raise StopIteration()
arg = self.__args[self.__index]
self.__index += 1
return arg
myiter = MyIter(1, 2, 3)
for arg in myiter:
print(arg)
実行結果
__iter__
__next__
1
__next__
2
__next__
3
__next__
StopIteration
他のクラスでも同じ__next__メソッドを使いたい場合は、そのためのクラスを作成すると同じような実装を何度もしなくて済みます。また、複雑になりすぎる場合も、外部に実装することでクラスをすっきりさせることができます。
iter関数を使う

iter関数は、オブジェクトに定義されている__iter__メソッドを呼び出します。
使い方
引数に指定するオブジェクトは、__iter__メソッドを定義しているオブジェクトでなければなりません。
iter(オブジェクト)
サンプル
以下のコードでは、iter関数を使って可変長引数(タプル)の__iter__メソッドを呼び出して、自らの__iter__メソッドで返しています。
class MyIter:
def __init__(self, *args):
self.__args = args
def __iter__(self):
return iter(self.__args)
myiter = MyIter(1, 2, 3)
for arg in myiter:
print(arg)
実行結果
1
2
3
これを少し応用することで、以下のように、インスタンス変数をまとめてfor文で取得できるようなクラスを作成できます。
class Person:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def __iter__(self):
# インスタンス変数をタプルにまとめて、イテレータオブジェクトとして返す
return iter((self.name, self.age, self.gender))
p = Person('田中太郎', '20', '男')
for arg in p:
print(arg)
実行結果
田中太郎
20
男
まとめ
この記事では、Pythonのイテラブルオブジェクトとイテレータについて解説しました。
- イテラブルオブジェクトとは「反復可能なオブジェクト」である。
- 以下の2つのメソッドを定義することでイテレータを実装できます。
- __next__メソッドが定義されているオブジェクトを返す__iter__メソッドの定義
- 要素にアクセスし、最後にStopIteration例外を送出する__next__メソッドの定義
これらのことを覚えておけば、イテレータを実装することはとても容易です。
それでは今回の内容はここまでです。ではまたどこかで〜( ・∀・)ノ