のーずいだんぷ

主に自分用メモですが、もしかしたら誰かの役に立つかもしれません

Pythonのジェネレータへの苦手意識をなくしたい

今日も一日一文法シリーズ、やる。

Pythonのリファレンスや、参考書を読んでこれは知らなかったなとか、うろ覚えだな…というものを個人的に紹介していく。 勉強中の方の役にたてば嬉しい。そんな趣旨ではじめた。 読者になってもらうと一緒に勉強できておすすめ(勧誘)

教材としてしばらく以下の本を使用する。 今回のトピックスでもここから見つけた。

入門 Python 3

入門 Python 3

初めて3日だけど他の言語も勉強したくなってきた… 一文法だから別言語でもいいだろう、たまにScalaにする。

ジェネレータとは

公式ドキュメントより…

ジェネレータオブジェクトは、 Python がジェネレータイテレータを実装するのに使っているオブジェクトです。 ジェネレータオブジェクトは通常、 PyGen_New() や PyGen_NewWithQualName() の明示的な呼び出しではなく、値を yield する関数のイテレーションにより生成されます。

PyGen_New()?ちょっと聞いたことないけど大事なのは前半と後半の

  • Python がジェネレータイテレータを実装するのに使っているオブジェクト
  • 値を yield する関数のイテレーションにより生成されます

が重要だが概念の説明は分かりにくいので特徴を抽出して並べると以下のような感じになる。

  1. listdictのように値を反復して取り出すことができる。
  2. 取り出している場所の情報を記録しており、途中から再開することができる。
  3. 再度まで要素を取り出すと、要素はもう取り出すことはできない。

ざっとこんなとこだろうか、順番に性質を確認していきたい。 その前に定義方法を確認する。

ジェネレータの定義方法

宣言

>>> def gen():
...     yield "gen"

上記のように関数に類似した宣言をするが、関数との差異はreturnyieldとなる点が異なる。

値の取り出し

>>> g = gen()
>>> next(g)
'gen'
>>> 

ここも関数と類似しており上記の場合gen()を実行するとジェネレータオブジェクトが生成される。 ジェネレータオブジェクトに対してnext()メソッドを使用すると値が取り出せる。

他にも以下のようにiter()によってイテレータをを取り出してイテレータをして使用できる。

ジェネレータの性質

性質1:listdictのように値を反復して取り出すことができる。

>>> def gen3(n):
...     for i in range(n):
...             yield i
... 
>>> ggg = gen3(3)
>>> next(ggg)
0
>>> next(ggg)
1
>>> next(ggg)
2
>>> next(ggg)

性質2:取り出している場所の情報を記録しており、途中から再開することができる。

>>> def gen5(n):
...     for i in range(n):
...             yield i
... 
>>> g = gen5(5)
>>> for i in range(2):
...     next(g)
... 
0
1
>>> for i in range(2):
...     next(g)
... 
2
3

性質3:再度まで要素を取り出すと、要素はもう取り出すことはできない。

>>> def gen3(n):
...     for i in range(n):
...             yield i
... 
>>> ggg = gen3(3)
>>> next(ggg)
0
>>> next(ggg)
1
>>> next(ggg)
2
>>> next(ggg)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

上記のように例外StopIterationが発生する。

ジェネレータはどういう場合に使うべきか

いくつかのサイトを参考にさせていただき、とりあえずの仕様方針は得られた。

  • メモリ消費を抑える(そもそも複数回使い回さないからlistである必要がない時)
  • 中断した点から再開したい

正直ほかサイトから得られる情報を精査するので精一杯だが、速度についてはケースバイケースのようだ。 参考に実践的な例が充実していてよい。 まだまだ実装力が不足しているようなので、今後はリストを使う場面ではジェネレータの使用を検討したい。

参考

Python: ジェネレータをイテレータから理解する - CUBE SUGAR CONTAINER

Pythonのジェネレーターってなんのためにあるのかをなるべく分かりやすく説明しようと試みた - Qiita

ジェネレーター — Pythonオンライン学習サービス PyQ(パイキュー)ドキュメント