2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-05-11

やりたいこと

イテレータとジェネレータに入門する。
forとイテレータ・ジェネレータの関係を知る。

イテレータ関連

シーケンス型

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

反復可能オブジェクト(イテラブルオブジェクト)

  • 要素をひとつずつ返すことができるオブジェクト。
  • イテレータ(後述)を返す関数__iter__()を持つ。
  • 以下のようなオブジェクトがイテラブルオブジェクトである。
    • シーケンス型(strlisttuplerangebytesbytearray)
    • 非シーケンス型(dictsetfile object)
  • 以下のようにして、イテラブルオブジェクトのイテレータを取得することができる。
sample_list = ['a', 'b']
# iter_a, iter_b はどちらも、sample_listのイテレータ
iter_a = iter(sample_list)
iter_b = sample_list.__iter__()

image.png

イテレータ

  • データの流れを表現するオブジェクト。
  • 以下ふたつの関数を持つ。
    • 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

image.png

ジェネレータ関連

ジェネレータ

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

ジェネレータイテレータ

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

ジェネレータ関数

  • イテレータを戻り値とする関数。
  • イテレータを簡単に作るのが目的。
    • ジェネレータの返り値は、自動で__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

image.png

イテレータ・ジェネレータとforの関係

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

for item in sample_generator():
    print(item)   # あ # い  # う

参考URL

2
3
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?