やりたいこと
イテレータとジェネレータに入門する。
forとイテレータ・ジェネレータの関係を知る。
イテレータ関連
シーケンス型
- 複数の値を順番に並べたものをひとかたまりとして格納するための型。
- 整数のインデックスを指定して、要素にアクセスできる。
- シーケンス型な型
- str、list、tuple、range、bytes、bytearray
- シーケンス型であれば必ずイテラブルオブジェクト(後述)であるが、逆は成り立たない。
- どの型がシーケンスではないがイテラブルオブジェクトである型など、こちらのサイトがわかりやすかったです。
反復可能オブジェクト(イテラブルオブジェクト)
- 要素をひとつずつ返すことができるオブジェクト。
- イテレータ(後述)を返す関数
__iter__()
を持つ。 - 以下のようなオブジェクトがイテラブルオブジェクトである。
- シーケンス型(
str
、list
、tuple
、range
、bytes
、bytearray
) - 非シーケンス型(
dict
、set
、file object
)
- シーケンス型(
- 以下のようにして、イテラブルオブジェクトのイテレータを取得することができる。
sample_list = ['a', 'b']
# iter_a, iter_b はどちらも、sample_listのイテレータ
iter_a = iter(sample_list)
iter_b = sample_list.__iter__()
イテレータ
- データの流れを表現するオブジェクト。
- 以下ふたつの関数を持つ。
-
iterator.__iter__()
- イテレータオブジェクト自身を返す関数
-
iter(iterator)
も同じ。
-
iterator.__next__()
- イテレータの要素をひとつずつ取り出す関数
-
next(iterator)
も同じ。
-
-
iterator.__next__()
(もしくはnext(iterator)
)で、要素を一つずつ順番に取り出せる。- 取り出せる要素が尽きたとき、例外
StopIteration
を返す。
- 取り出せる要素が尽きたとき、例外
- 「
イテレータを返す関数__iter__()を持つ。
」という、イテラブルオブジェクトの定義を満たすため、イテレータもイテラブルオブジェクトの一種である。- イテラブルオブジェクトとイテレータの関係は、こちらのサイトがわかりやすかったです。
sample_list = ['a', 'b', 'c']
# 2種類の方法でリストのイテレータを取得
sample_iter1 = iter(sample_list)
sample_iter2 = sample_list.__iter__()
# iter()で取得したイテレータを用いて要素を取得
print(next(sample_iter1)) # a
print(next(sample_iter1)) # b
# __iter__()で取得したイテレータを用いて要素を取得
print(sample_iter2.__next__()) # a
print(sample_iter2.__next__()) # b
print(sample_iter2.__next__()) # c
print(sample_iter2.__next__()) # StopIteration
ジェネレータ関連
ジェネレータ
- 文脈により、ふたつの意味が考えられる
- ジェネレータイテレータ
- ジェネレータ関数
ジェネレータイテレータ
- 「ジェネレータオブジェクト」とも言う。
- ジェネレータ関数で生成されたイテレータ。
- ジェネレータ関数の戻り値がイテレータとなっている。
ジェネレータ関数
- イテレータを戻り値とする関数。
- イテレータを簡単に作るのが目的。
- ジェネレータの返り値は、自動で
__iter__()
と__next__()
が備えられる(=イテレータである)
- ジェネレータの返り値は、自動で
- 関数の中に
yield
が含まれている。
以下のようなジェネレータ関数の場合を考える。
def sample_generator():
sample_list = ['あ', 'い', 'う']
yield sample_list[0]
yield sample_list[1]
yield sample_list[2]
上記のジェネレータ関数の戻ち値が持つ関数をhelp()
で見てみると、
__next__()
、__iter__()
が含まれていることがわかる。
これは、オブジェクトがイテレータであるための条件を満たしている。
g = sample_generator()
help(g)
"""
Help on generator object:
sample_generator = class generator(object)
| (省略) |
| __iter__(self, /)
| Implement iter(self).
|
| __next__(self, /)
| Implement next(self).
(省略)
"""
イテレータは、__next__()
(もしくはnext()
)が呼び出されるたびに、yield
に指定された値を順番に返すため、
次のいずれかの方法で、要素を取得することができる。
f = sample_generator()
print(next(f)) # あ
print(next(f)) # い
print(next(f)) # う
print(next(f)) # StopIteration
f = sample_generator()
print(f.__next__()) # あ
print(f.__next__()) # い
print(f.__next__()) # う
print(f.__next__()) # StopIteration
イテレータ・ジェネレータとforの関係
forのin句にイテラブルオブジェクト(イテレータを含む)を渡すと、
そのオブジェクトの__iter__()
が呼び出され、イテレータが返されます。
そしてそのイテレータの__next__()
を呼び出し要素を取り出したら、
for内の処理を実行します。
その後も順に「__next__()
→for内処理」を繰り返し、
イテレータの要素が尽き、例外StopIteration
を検知すると、自動で処理が止まります。(例外は発生しない)
for item in sample_generator():
print(item) # あ # い # う