クラス

【Python】イテレータ(iterator)の基本

【Python】イテレータ(iterator)の基本

Pythonにおけるイテレータ(iterator)の基本について解説します。

イテレータ(iterator)

イテレータ(iterator)とは

イテレータ(iterator)とは、繰り返し処理に必要な特殊メソッドを実装したオブジェクトで、イテラブルオブジェクトの要素を順に取り出すために使用されます。ここで言っている特殊メソッドとは具体的に、イテレータプロトコルを実現する以下の2つのメソッドです。

メソッド名概要
__next__()コンテナの次の要素を返す
__iter__()イテレータ自身を返す

Pythonを学んでいるとイテラブル(iterable)という言葉を聞くことがあるかと思います。イテラブルとは、for文で要素として1つずつ取り出せるような反復可能なオブジェクトのことで、代表的なものとしてリスト(list)、タプル(tuple)、辞書(dict)等があります。__iter__()メソッドかもしくは__getitem__()メソッドを持つ任意のクラスのオブジェクトもイテラブルです。イテラブル(iterable)に関する公式ドキュメントの説明はこちらを参照してください。

イテレータは、イテラブルオブジェクトから__iter__()メソッドを使って生成されるオブジェクトであり、__next__()メソッドを通じて順に要素を取り出すことができるものです。

組み込み関数のiter()に以下の例のようにシーケンス(文字列等)を渡すとイテレータを作成することができます。1つずつ値を取り出せることを見てみましょう。

sample = "python"

# イテレータを作成
i = iter(sample)
print(type(i))

# 値を順に取り出す
print(next(i))
print(next(i))
print(next(i))
print(next(i))
print(next(i))
print(next(i))
# 全て取り出し終わるとStopIteration例外となる
print(next(i))
【実行結果】
<class 'str_ascii_iterator'>
p
y
t
h
o
n
Traceback (most recent call last):
...(省略)...
StopIteration

上記のようにiter関数に文字列を渡すとstr_ascii_iteratorというイテレータが作成されます。このイテレータを通じて値の順次取り出しが可能です。

イテレータは、next関数に渡すと順に値を取り出すことができ、上記の例では"p", "y", "t", "h", "o", "n"と順に文字列の値が取り出されていることが分かります。

値を全て取り出し終わると、イテレータはStopIteration例外を送出します。for文ではこのStopIteration例外を自動でとらえてループが終了するようになっているので、イテレータはfor文で使用することができます。

独自のカスタムイテレータ(iterator)の作成

イテレータ(iterator)は、特殊メソッドの__next__()メソッドと__iter__()メソッドを実装したコンテナオブジェクトでした。これらのメソッドを自分で実装することで、独自のカスタムイテレータを作成することができます。

例えば、数値をカウントダウンするMyCountDownクラスという簡単なクラスを作ってみましょう。

class MyCountDown:
    """カウントダウンイテレータ"""

    def __init__(self, count):
        self.count = count

    def __next__(self):
        """次の値を返す"""
        # 繰り返しの終わりではStopIteration例外を送出する
        if self.count <= 0:
            raise StopIteration
        # カウントダウン
        self.count -= 1
        # 次の値を返す
        return self.count

    def __iter__(self):
        """イテレータの状態を返す"""
        # イテレータ自身を返却
        return self


def main():
    count_iter = MyCountDown(5)
    for c in count_iter:
        print(c)


if __name__ == "__main__":
    main()
【実行結果】
4
3
2
1
0

上記のMyCountDownクラスの__next__()メソッドでは、count1減らして値を返却します。もしマイナスの値になった際にはStopIteration例外を送出(raise)するようになっています。また、__iter__()メソッドでは、イテレータ自身のselfを返却しています。

main関数内でMyCountDownクラスのオブジェクトを作成し、for文で使用しながら値を出力しています。値のカウントダウンができ、終了時にはfor文を抜けていることが分かります。

このように__next__()メソッドや__iter__()メソッドを自分で作成することで独自のカスタムイテレータを作成することが可能です。

itertools ~効率的なループ実行のためのイテレータ生成関数~

Pythonのライブラリにイテレータの使い方としてよくあるパターンを実装したitertoolsと呼ばれるライブラリがあります。itertoolsを使うことでイテレータを簡単に活用することが可能です。

for文でよく使われるzipの説明をしている「zipを用いたfor文の使い方 ~複数のリストをまとめて処理~」のページでも、itertoolszip_longestを紹介しています。通常のzipが要素の少ない方にあわせて処理をするのに対して、itertoolszip_longestは要素数が多い方にあわせて足りない部分を補完して処理したい場合に使われます。

itertoolsには、他にも実装されているものがあるので、また整理してみようかなと思います。

Note

itertoolsの公式ドキュメントはこちらを参照してください。

まとめ

Pythonにおけるイテレータ(iterator)の基本について解説しました。

イテレータ(iterator)とは、繰り返しに必要な特殊メソッドを実装したイテラブル(iterable)なコンテナオブジェクトの事を言います。

イテレータは順番に要素を1つずつ取り出すことができfor文で利用することができます。また、イテレータは__next__()メソッドや__iter__()メソッドを自分で実装することで独自のカスタムイテレータを作成することも紹介しました。

イテレータをあえて使用しなくてもプログラム開発ができるわけですが、イテレータはジェネレータの基礎にもなっています。ジェネレータについては「ジェネレータ(generator)関数 ~yieldによる返却~」でも紹介していますので参考にしてください。