Python のイテレータとイテラブルの違いまとめ

DALLE-2024-08-20

Python の似た 2 つの概念であるイテレータ( iterators )とイテラブル( iterables )についてまとめました。

ポイント

  • イテレータ: 一連のデータを内部状態とともに管理するオブジェクト。 next() に渡して使える。
  • イテラブル: コレクション型のオブジェクトで 1 つずつ返す機能を持つもの。 iter()for ループに渡して使える。
  • b = iter(a) と書けるとき、 a はイテラブルであり、 b はイテレータである。つまり、イテラブルからイテレータを生成できる。
  • イテレータ自身を iter() に渡せるので、すべてのイテレータはイテラブルでもある。
  • その逆は必ずしも真ではない。つまり、イテラブルは一般にイテレータではない。イテレータは next() に渡せるが、イテラブルは next() に渡せない。
  • イテラブルではあるがイテレータではないオブジェクトの例は listalist = [1, 2, 3] のとき、 iter(alist) は動くが next(alist) はエラーになる。

見分けるポイント

  • 内部状態を持っているものはイテレータ。持っていないものはイテラブル。
  • next() に渡して使えるものはイテレータ。使えないものはイテラブル。

イテレータとは

一連のデータを内部状態を持って管理するためのオブジェクト。 イテレータは組み込み関数 next() に渡して使える。 独自に定義されたクラスのオブジェクトの場合は 2 つのメソッド __next__()__iter__() を持つ必要がある。 __iter__() を持っているため、すべてのイテレータはイテラブルである。

イテレータの例:

  • 組み込み
    • range() オブジェクト
    • zip() オブジェクト
    • enumerate() オブジェクト
    • ファイルオブジェクト
  • ユーザー定義クラスのオブジェクト
    • 2 つのメソッド __iter__()__next__() を持つオブジェクト

イテラブルとは

コレクション型のオブジェクトで、要素を 1 つずつ返す機能を持つオブジェクト。 iter()for ループに渡して使える。 独自に定義されたクラスのオブジェクトの場合は __iter__()__getitem__() を持つ必要がある。

イテラブルの例:

  • 組み込み
    • list: [1, 2, 3]
    • dict: {'a': 1, 'b': 2}
    • str: "Hello"
  • ユーザー定義クラスのオブジェクト
    • __iter__() を持つオブジェクト
    • __getitem__() を持つオブジェクト

ジェネレータ関数の戻り値のタイプヒントは IteratorIterable のどちらにすべきか?

ジェネレータ関数( generator )とは yield 文を使って定義された関数です。 ジェネレータ関数が返すオブジェクトはジェネレータイテレータ( generator iterator )と呼ばれます。

ジェネレータ関数の戻り値のタイプヒントで Iterator を使うべきか Iterable を使うべきか。 Python の公式ドキュメンテーションで答えを探したのですが見つかりませんでした。

静的チェックではどちらで書いても問題ないことがありますがが、ジェネレータイテレータは next() に渡して使えるイテレータなのでどちらかというと Iterator を使う方がよいかと思います(絶対にそうだとまでは言い切れません)。

サンプル:

from typing import Iterator


# generator の戻り値のタイプヒントに `Iterator` を使う:
def yield_numbers(start: int, stop: int) -> Iterator[int]:
    assert start < stop

    value = start
    while value < stop:
        yield value
        value += 1


iter1 = yield_numbers(5, 13)
print(next(iter1))  # => 5
print(next(iter1))  # => 6

IteratorIterable かで迷う場合は typing.Generator を使うのでもよいと思います。

引用: Python 公式の用語集の説明

イテレータ

An object representing a stream of data. Repeated calls to the iterator’s __next__() method (or passing it to the built-in function next()) return successive items in the stream. When no more data are available a StopIteration exception is raised instead. At this point, the iterator object is exhausted and any further calls to its __next__() method just raise StopIteration again. Iterators are required to have an __iter__() method that returns the iterator object itself so every iterator is also iterable and may be used in most places where other iterables are accepted. One notable exception is code which attempts multiple iteration passes. A container object (such as a list) produces a fresh new iterator each time you pass it to the iter() function or use it in a for loop. Attempting this with an iterator will just return the same exhausted iterator object used in the previous iteration pass, making it appear like an empty container.

iterator | Glossary — Python documentation

イテラブル

An object capable of returning its members one at a time. Examples of iterables include all sequence types (such as list, str, and tuple) and some non-sequence types like dict, file objects, and objects of any classes you define with an __iter__() method or with a __getitem__() method that implements sequence semantics.

Iterables can be used in a for loop and in many other places where a sequence is needed (zip(), map(), …). When an iterable object is passed as an argument to the built-in function iter(), it returns an iterator for the object. This iterator is good for one pass over the set of values. When using iterables, it is usually not necessary to call iter() or deal with iterator objects yourself. The for statement does that automatically for you, creating a temporary unnamed variable to hold the iterator for the duration of the loop.

iterable | Glossary — Python documentation

参考