LoginSignup
8
17

More than 3 years have passed since last update.

【Python】ジェネレータ(generator)関数

Last updated at Posted at 2020-09-10

はじめに

 この記事はジェネレータ(generator)関数を理解せずに使用して失敗したため, ジェネレータ(generator)関数の勉強のための記事になります. 内容は, 参考文献を用いた自分用の備忘録です.

定義

 まずはジェネレータ関数の定義を確認する.

  • 定義:ジェネレータ関数1

関数定義の中でyield文が使われている場合、その関数はジェネレータ関数と呼ばれます。ジェネレータ関数はイテレータの一種です。

例:
 yieldを使ったジェネレータ関数をPython3.7.4で実行. yieldにより区切られて値が返されます.

def sample_generator_fun():
    yield 1
    yield 2
    yield 3

check = sample_generator_fun()
print(check.__next__())
print(check.__next__())
print(check.__next__())

 実行結果

1
2
3

 さらにcheckを実行すると...

print(check.__next__())
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-25-7029da5797f2> in <module>
----> 1 print(check.__next__())

StopIteration:

 反復停止と表示され, エラーとなります. つまり, sample_generator_funのyield文が順に実行され, 3番目の「yield 3」が実行されると反復が終了され, さらに実行しても値を返しません.

失敗例

 ジェネレータ関数をきちんと理解せず, リストなどのイテレータオブジェクトと同じものとして考え, 下記の様な失敗をしました. ※そもそも, 恥ずかしながらジェネレータ関数の存在を知りませんでした

例:
 取得されたジェネレータ関数のイテレータ数が2超であるのかを確認し, そうであれば実行する.

 失敗例

check = sample_generator_fun()

if len(list(check)) > 2:
    print("True")
    for i in check:
        print(i)
else:
    print("False")

 実行結果

True

 ここで, 私はif文でTrueになっているのに, なぜその下のfor文が実行されないのか悩み, 色々と調べてジェネレータ関数にたどり着くことが出来ました. 条件式内のlist(check)を実行したことで, 最後の「yield 3」まで実行されたことになるので, 次のfor文ではcheckは値を返さないため, for文は実行されません.

解決策

 簡単に思いつく範囲で下記2つが挙げられます. 工夫してジェネレータ関数を複数回呼び出し可能にする場合は, この記事2の様にするのが良いかもしれません.

  • ジェネレータ関数で返される総データサイズが大きくなければ, リストに変換して使用する
check = sample_generator_fun()
check_list = list(check)

if len(check_list) > 2:
    print("True")
    for i in check_list:
        print(i)
else:
    print("False")

 実行結果

True
1
2
3
  • 単純に2回, ジェネレータ関数を呼び出す. この場合はジェネレータ関数のメリットであるメモリの節約は保持されます
check = sample_generator_fun()

n = 0
for i in check:
    n += 1
    if n > 2:
        print("True")
        check = sample_generator_fun()
        for j in check:
            print(j)
        break
else:
    print("False")

 実行結果

True
1
2
3

感想

 スクリプト言語では型宣言などをしなくてもコードを書けますが, どんな型やどんな性質のオブジェクトを触っているのかは, きちんと把握していないと事故りますね. 今回は「リスト」と同様だろうなという希望的観測による失敗でした. 使用するライブラリの中身を全て把握するのは難しいですが, 今回のような基本的なことはきちんと正確に抑えるべきだと改めて痛感しました.


  1. 中久喜健司:科学技術計算のためのPython入門, 2016年, 技術評論社 

  2. Python のジェネレータを何回もイテレートしたい 

8
17
4

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
8
17