イテレータとジェネレータについて
以前、ジェネレータの例についてブログにメモをしておきました。
-
-
ジェネレータ
ジェネレータ 関数の処理の途中で中断して、yieldを使う事で関数外の処理を実施しても、再びyieldの次から再開できる。 どうやったら効果を発揮するのだろうか? 実装例 def gen(): pri ...
続きを見る
が、今、振り返ると私自身、ジェネレータの意味が良く理解できていない事がわかりました。
再度、ジェネレータについて考えてみたいと思っています。
そして、ジェネレータを理解するにはイテレータから理解する必要があるという事もわかりました。
イテレータ
まず、イテレータとは何でしょうか?
イテレータとは、連続するデータを操作するオブジェクトの事です。
連続するデータとは例えば、リストや集合、タプルなどですね。
例えば連続データを処理する時に、100万件のレコードを処理する必要があったケースをイメージしてみます。
一度、100万件のレコードを全部取り込んだ後に処理するよりも、1件ずつレコードを処理して処理後に破棄、また次のレコードを取り込んで処理後に破棄。
このような処理を行った方が効率が良いケースがあります。
こういった場合にイテレータを利用するとPythonの枠組みの中で便利だったりするわけです。
例えば、for 文を使う事が多々あると思うのですが、実は for 文の裏側で動いているのがイテレータです。
以下は for 文を自作した例です。
l = [1, 2, 3, 4, 5]
iterator = iter(l)
try:
l1 = next(iterator)
print(l1)
l2 = next(iterator)
print(l2)
l3 = next(iterator)
print(l3)
l4 = next(iterator)
print(l4)
l5 = next(iterator)
print(l5)
l6 = next(iterator)
print(l6)
except StopIteration:
print("ループが終了します。")
リスト l の要素を、iter 関数を利用して変数 iterator に代入しました。
その後、next 関数で順番に1つ1つ要素を代入して出力しています。
今回の例では、要素が5つしかないので、6つ目の要素を代入しようとした時に例外 StopIteration が発生した為、ループが終了する事になります。
これが for 文の仕組みです。
ちなみにPythonは、 「__next__() と __iter__() メソッドを持っているオブジェクトをイテレータである。」と定義しています。
変数 iterator を dir 関数で対象の属性を出力してみると以下の結果が確認できます。
[
'__class__',
'__delattr__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__iter__',
'__le__',
'__length_hint__',
'__lt__',
'__ne__',
'__new__',
'__next__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__setstate__',
'__sizeof__',
'__str__',
'__subclasshook__'
]
ジェネレータ
イテレータの考え方を理解して、ようやくジェネレータに移る事ができます。
ジェネレータとは、「イテレータを簡単に作れる関数」です。
以下にジェネレータを作成してみます。
def generator():
x = 1
yield x
x += 1
yield x
x += 1
yield x
g = generator()
print("next 1 回目")
next(g)
print("next 2 回目")
next(g)
print("next 3 回目")
next(g)
"""
### 出力内容 ###
next 1 回目
next 2 回目
next 3 回目
"""
ちゃんと理解するのが難しい部分ではありますが、ジェネレータを理解する上で、イテレータの概念を理解する事は、とても重要な事だと考えております。