Pythonにおけるイテレータとジェネレータについて学ぶ

2 min読了の目安(約2400字TECH技術記事

イテレータ

イテレータは以下のような位置にくるオブジェクトを指す。

for x in イテレータ:
    xの処理

イテレータは複数のメソッド(プロトコル)を定義している必要がある。
__iter__メソッドと__next__メソッドである。

__iter__メソッドはイテレータオブジェクトを返すことを期待している。
このイテレータオブジェクトはnext関数を呼び出せるように__iter__メソッドを実装している必要がある。

そして、__next__メソッドをオブジェクトに実装することでイテレータオブジェクトとなり、forのような反復処理を実装できる。
StopIteration例外を送ることで、データの読み出しが終わったことを示す。

たとえば、以下のコードは2乗を行うイテレータである。

class SquareIterator:

    def __init__(self, *args):
        self.args = args
        self.i = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.i == len(self.args):
            raise StopIteration()
        ret = self.args[self.i] ** 2
        self.i += 1
        return ret


sit = SquareIterator(10, 20, 30)
for x in sit:
    print(x)

ジェネレータ

yieldで値を返すメソッドのことである。
__next__プロトコルを実装することができる。(どうやら暗黙的に呼び出されるらしい。)

ジェネレータを用いる理由としては、リストのように前もってすべて計算するとメモリ的に厳しいなどがある。
全体をすべて持つことは厳しいため、1つ要素を触るごとに呼び出しごとに返すなどが行える。

import random
def generator():
    while True:
        # 本当はここで画像を読むなどの重い処理がなされる
        yield random.randint(0, 10)


for x in generator():
    print(x)

Type Hints

Iteratorの型ヒントは以下のように行う。

from typing import Iterator

class SquareIterator:

    def __init__(self, *args):
        self.args = args
        self.i = 0

    def __iter__(self) -> Iterator[int]:
        return self

    def __next__(self):
        if self.i == len(self.args):
            raise StopIteration()
        ret = self.args[self.i] ** 2
        self.i += 1
        return ret


sit: Iterator = SquareIterator(10, 20, 30)

ジェネレータは以下のように行う。
Generator[YieldType, SendType, ReturnType]という形式になっている。
今回は値を返すだけなので、SendTypeとYieldTypeが必要ない。

from typing import Generator
import random


def generator() -> Generator[int, None, None]:
    while True:
        yield random.randint(0, 10)


for x in generator():
    print(x)

ただし、これらIterator, Generatorは3.9以降では[]で書くことが推奨されている。

イテラブル

イテラブル (Iterable)は、forなどで繰り返せるオブジェクトを指す。
たとえば、リスト、セットなどがイテラブルである。
__iter__メソッドもしくは__getitem__メソッドを定義しているオブジェクトである。

イテラブルのオブジェクトから__iter__メソッドを読んでイテレータを取り出す。(これは、iter関数によって、実行される。)
このイテレータとは__next____iter__メソッドをプロトコルとして実装しているオブジェクトである。
そして、このイテレータをnext関数にわたすことによって、データを順次取り出しすることが可能である。

参考URL