Pythonのジェネレータをわかりやすく解説

ジェネレータpython
  • ジェネレータはfor文で利用できるイテラブルなオブジェクト
  • リストやタプルが要素をメモリ上に保持するのと異なり、ジェネレータは次の要素が求められるたびに新たな要素を生成して返す。それ故、要素数にかかわらずメモリ使用量を小さくできる

 

スポンサーリンク

ジェネレータの具体例

値を無限に返し続けるジェネレータ「inf」を作成した例です。

 

パッと見普通の関数と同じように見えますが、内部にある“yield式”がジェネレータの目印です。

def inf(n):
    while True:
        yield n

実行すると引数として渡した値を出力し続けます。

要素を無限に返すこのような動きは、全ての要素をメモリに保持するリストやタプルでは実現できません。

 

ジェネレータの作り方

ジェネレータの作り方は次の2つ

  1. ジェネレータ関数
  2. ジェネレータ式
1.ジェネレーター関数(関数のように作成)

内部でyield式を使っている関数のことをジェネレータ関数といいます。

 

ジェネレータ関数の戻り値はイテレータで、その特殊メソッド_next_()が呼ばれるたびに関数内の処理が次のyield式まで進む。

呼び出し元に、yield式に渡した値を返すと、その時の状態を保持したままその行で処理を中断する。

再度_next_()が呼ばれるとその行から処理が再開され関数を抜けると自動でStopIterationが送出される。

 

def gen_function(n):
    print('start')
    while n:
        print(f'yield: {n}')
        yield n  #ここで一時中断される
        n -= 1
-----------------------------------------
gen = gen_function(2)
next(gen)  #組み込み関数next()に渡すと_next_()が呼ばれる
-----------------------------------------
#出力
start
yield: 2
2
-----------------------------------------
next(gen)
-----------------------------------------
#出力
yield: 1
1
-----------------------------------------
next(gen)
-----------------------------------------
#出力
StopIteration                      Traceback (most recent call last)
  in 
----> 1 next(gen)

StopIteration:

(エラー出力は簡略化して書いてます。)

2.ジェネレータ式(内包表記を利用して作成)

リストやタプルなどのイテラブルなオブジェクトがあると、内包表記を使ってそのイテラブルなオブジェクトからジェネレータを作ることができます。

そのようなジェネレータの作り方をジェネレータ式といい、リスト内包表記と同じ構文で[]の代わりに()を使って書きます。

 

x = [1, 2, 3, 4, 5]
gene = (i**2 for i in x)  #これがジェネレータ式
-----------------------------------------
next(gen)
-----------------------------------------
#出力
1
-----------------------------------------
gen[4]  #各要素は必要になるまで計算されない!
-----------------------------------------
#出力
TypeError                                 Traceback (most recent call last)
 in 
----> 1 gen[4]

TypeError: 'generator' object is not subscriptable

この例からわかるように、ジェネレータは必要になるまで計算されません。
なので添字では呼ぶことができませんね(not subscriptable)。

ジェネレータの注意点

組み込み関数len()が使えない!

”ジェネレータは必要になるまで計算されない”と関係しています。

def gen(n):
    while n:
        yield n
        n -= 1
-----------------------------------------
len(gen(5))
-----------------------------------------
TypeError                                 Traceback (most recent call last)
 in 
----> 1 len(gen(5))
TypeError: object of type 'generator' has no len()
ジェネレータを複数回利用する場合

ジェネレータは“状態を保持する”ということを忘てはいけません。

以下のように、「list(g)」でgをリストとして展開しているから(つまりそのタイミングになるまで計算しない、のではなくリストとして一気に最後まで計算している)、2回目以降の結果は常に0になります。

 

g = gen(4)
-----------------------------------------
len(list(g))  #リストに展開(=最後まで計算)
-----------------------------------------
#出力
4
-----------------------------------------
len(list(g))
-----------------------------------------
#出力
0

 

参考書籍:

『Python実践入門ー言語の力を引き出し、開発効率を高める』

陶山嶺 著

WEB+DB PRESS plusシリーズ

技術評論社

Python実践入門 ──言語の力を引き出し、開発効率を高める
Pythonはここ数年で日本語の書籍も増え,開発現場での利用実績も着実に増えてきています。ご自身の第二,第三の言語の選択肢としてPythonが気になっているという方も多いのではないでしょうか。また,「Pythonを始めてみたけど,実際に業務で利用するには不安が残る」「コードレビューに怯えながらPythonを書いている」...

 

タイトルとURLをコピーしました