イテレータとジェネレータについて
以前、ジェネレータの例についてブログにメモをしておきました。
-
ジェネレータ
ジェネレータ 関数の処理の途中で中断して、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 回目 """
ちゃんと理解するのが難しい部分ではありますが、ジェネレータを理解する上で、イテレータの概念を理解する事は、とても重要な事だと考えております。