イテレータとジェネレータに入門する(forとの関係も)【Python】

# やりたいこと
イテレータとジェネレータに入門する。
forとイテレータ・ジェネレータの関係を知る。

# イテレータ関連
## シーケンス型
- 複数の値を順番に並べたものをひとかたまりとして格納するための型。
- 整数のインデックスを指定して、要素にアクセスできる。
- シーケンス型な型
    - str、list、tuple、range、bytes、bytearray
- シーケンス型であれば必ずイテラブルオブジェクト(後述)であるが、逆は成り立たない。
    - どの型がシーケンスではないがイテラブルオブジェクトである型など、[こちら](https://gammasoft.jp/blog/python-built-in-types/)のサイトがわかりやすかったです。



## 反復可能オブジェクト（イテラブルオブジェクト）
- 要素をひとつずつ返すことができるオブジェクト。
- イテレータ(後述)を返す関数`__iter__()`を持つ。
- 以下のようなオブジェクトがイテラブルオブジェクトである。
    - シーケンス型(`str`、`list`、`tuple`、`range`、`bytes`、`bytearray`)
    - 非シーケンス型(`dict`、`set`、`file object`)
- 以下のようにして、イテラブルオブジェクトのイテレータを取得することができる。
```py
sample_list = ['a', 'b']
# iter_a, iter_b はどちらも、sample_listのイテレータ
iter_a = iter(sample_list)
iter_b = sample_list.__iter__()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320845/2e9e78a2-20ee-2552-f1ae-cce0b826ea4f.png)



## イテレータ
- データの流れを表現するオブジェクト。
- 以下ふたつの関数を持つ。
    - **`iterator.__iter__()`**
        - イテレータオブジェクト自身を返す関数
        - `iter(iterator)`も同じ。
    - **`iterator.__next__()`**
        - イテレータの要素をひとつずつ取り出す関数
        - `next(iterator)`も同じ。
- `iterator.__next__()` （もしくは`next(iterator)`）で、要素を一つずつ順番に取り出せる。
    - 取り出せる要素が尽きたとき、例外 `StopIteration` を返す。
- 「`イテレータを返す関数__iter__()を持つ。`」という、イテラブルオブジェクトの定義を満たすため、イテレータもイテラブルオブジェクトの一種である。
    - イテラブルオブジェクトとイテレータの関係は、[こちら](https://coroconlab.com/iterable_object/)のサイトがわかりやすかったです。


```py
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
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320845/119d044f-aab4-2cf7-0d9d-642e3a768506.png)


# ジェネレータ関連

## ジェネレータ
- 文脈により、ふたつの意味が考えられる
    - ジェネレータイテレータ
    - ジェネレータ関数


## ジェネレータイテレータ
- 「ジェネレータオブジェクト」とも言う。
- ジェネレータ関数で生成されたイテレータ。
    - ジェネレータ関数の戻り値がイテレータとなっている。


## ジェネレータ関数
- イテレータを戻り値とする関数。
- イテレータを簡単に作るのが目的。
    - ジェネレータの返り値は、自動で`__iter__()`と`__next__()`が備えられる（＝イテレータである）
- 関数の中に`yield`が含まれている。

以下のようなジェネレータ関数の場合を考える。
```py
def sample_generator():
    sample_list = ['あ', 'い', 'う']
    yield sample_list[0]
    yield sample_list[1]
    yield sample_list[2]
```

上記のジェネレータ関数の戻ち値が持つ関数を`help()`で見てみると、
`__next__()`、`__iter__()`が含まれていることがわかる。
これは、オブジェクトがイテレータであるための条件を満たしている。
```py
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`に指定された値を順番に返すため、
次のいずれかの方法で、要素を取得することができる。

```py
f = sample_generator()
print(next(f))  # あ
print(next(f))  # い
print(next(f))  # う
print(next(f))  # StopIteration
```
```py
f = sample_generator()
print(f.__next__())  # あ
print(f.__next__())  # い
print(f.__next__())  # う
print(f.__next__())  # StopIteration
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320845/a7469f00-77b4-0e2d-fee7-e2013a0c6bce.png)

# イテレータ・ジェネレータとforの関係
forのin句にイテラブルオブジェクト（イテレータを含む）を渡すと、
そのオブジェクトの`__iter__()`が呼び出され、イテレータが返されます。
そしてそのイテレータの`__next__()`を呼び出し要素を取り出したら、
for内の処理を実行します。
その後も順に「`__next__()`→for内処理」を繰り返し、
イテレータの要素が尽き、例外`StopIteration`を検知すると、自動で処理が止まります。（例外は発生しない）
```py
for item in sample_generator():
    print(item)   # あ # い　 # う
```



# 参考URL
- [公式ドキュメント 用語集　iterator](https://docs.python.org/ja/3/glossary.html#term-iterator)
- [イテラブル(反復可能)なオブジェクトとイテレータ](https://coroconlab.com/iterable_object/)
- [Pythonの組み込みデータ型の分類表（ミュータブル等）](https://gammasoft.jp/blog/python-built-in-types/)
- [[Python]イテラブル、イテレーター、ジェネレーターをざっくり理解する](https://qiita.com/KazukiTerashima/items/d3ca8f10cd5a302d62d8)
- [SIerだけど技術やりたいブログ Python for文とイテレータとジェネレータ](https://www.kimullaa.com/entry/2019/12/01/144434)
- [Python-izm ジェネレータ](https://www.python-izm.com/advanced/generator/)
- [公式ドキュメント　イテレータ型](https://docs.python.org/ja/3/library/stdtypes.html#iterator-types)
- [イテレータとは](https://atmarkit.itmedia.co.jp/ait/articles/1911/12/news011.html)