Python ジェネレーターの使い方

Python学習【365日チャレンジ!】58日目のマスターU(@Udemy11)です。

Generatorを辞書で調べると、発電機、発生させる人という意味ですが、Pythonでの使い方はちょっと使い方が違うような気がしないでもありません。

デコレーターについては、言葉の意味がPythonでの使い方とぴったりな感じがしましたが、Generatorは、イメージと一致しないので、新たなイメージを持っておいたほうがいいかもしれませんね。

それでは、今日の学習に入っていきましょう。

昨日の復習

昨日は、ラムダを学習しました。
名前をつけない関数、インスタント関数という表現を使いましたが、簡単な処理ならラムダを使うことで関数の代わりができました。

l = ['Sun', 'mon', 'tue', 'wed', 'Thu', 'fri', 'Sta']
def day_func(words, func):
    for word in words:
        print(func(word))
 
day_func(l, lambda word: word.capitalize())

出力結果

Sun
Mon
Tue
Wed
Thu
Fri
Sta

オレンジ色のコードがラムダで、day_funcを実行する前に、名前をつけた関数に定義して動作させるところを、lambdaを使うことで2行分を省略することができました。

ラムダについては、こちらの記事を参考にしてください。

それでは、今日のPython学習、ジェネレーターの学習にすすみましょう!

ジェネレーターとは

ジェネレーターは、発電機という意味ですが、Pythonではちょっと使い方のイメージと合致していないような気がします。

そんなジェネレーターの働きについてみていきましょう!

まず、forループで次のリストを順番に出力してみます。

g = ['Good morning', 'Good afternoon', 'Good evening']
for i in l:
    print(i)

出力結果

Good morning
Good afternoon
Good evening

forループの使い方は、何度も繰り返しているので、マスターできているかと思います。

リストに入った値を順番に取り出して出力しました。

この動作と同じように、関数に入れておいた値を順番に出力できるのがジェネレーターになります。

def greeting():
    yield 'Good morning'
    yield 'Good afternoon'
    yield 'Good evening'
 
for g in greeting():
    print(g)

出力結果

Good morning
Good afternoon
Good evening

関数greetingに、yield(イールド)を使って値を入れて、greetingを実行した値をforループで出力しています。

yieldは産み出すとかもたらすという意味を持っていますので、このあたりの意味からジェネレーターにつながるのかなと思いました。

出力結果は、リストから出力したときと同じになります。

一つづつ処理が完了する

リストで出力したときとの違いは、関数にしているというところですが、一番の大きな違いは、yieldを使って関数にすることで、一つの値を切り分けて扱えるというところです。

イマイチ意味がわからないと思うので、実際にやってみましょう!

def greeting():
    yield 'Good morning'
    yield 'Good afternoon'
    yield 'Good evening'
 
g = greeting()
print(next(g))

出力結果

Good morning

関数greetingの値を変数gに入れてnextメソッドを使って出力してみると、最初の値Good morningだけが出力されます。

ちなみに、変数gはイテレーターとなるので、出力してみると次のようになります。

<generator object greeting at 0x109f77570>

ジェネレーターオブジェクトになって、値が入った状態になっています。

このイテレーターを順番に取り出すのがnextメソッドで、最初の値を取り出して出力しているので、Good morningだけが表示されています。

print(next(g))を実行すれば、次の値を出力することができます。

ちなみに、関数greetingに入っているyieldがすべて使われたあとは、値がなくなるので、print(next(g))を実行するとエラーが返されます。

間に別の実行ができる

forループだと、たくさんの処理を一気にしてしまうので、必要に応じて順番に処理をさせたいというときに、ジェネレーターが使われます。

ジェネレーターの説明のために簡単な例を示しているので、この程度の処理で動作が重くなるということはありませんが、途中で一旦中断して別処理をさせたいというようなときに利用されているとのことです。

例えば、次のようなコードを書けば、処理の間に別のことを出力することができます。

def greeting():
    yield 'Good morning'
    yield 'Good afternoon'
    yield 'Good evening'
 
g = greeting()
print(next(g))
 
print('insert first sentence')
 
print(next(g))
 
print('insert second sentence')
 
print(next(g))

出力結果

Good morning
insert first sentence
Good afternoon
insert second sentence
Good evening

この例がわかりやすいかどうかは別として、forループではちょっとできないですよね。

もちろん出力内容が決まっているのなら、リストに値を挿入すればできるわけですが、たとえで使っているだけなので、実際に使う場面でこのような単純な動作をさせるわけではありません。

ジェネレーターは、繰り返しデータを取り出すことができるイテレーターの一つですが、forループのように、一気に値を処理するのではなく、一つ一つの値を切り分けて処理できるということをおぼえておきましょう。

ちょっとした違いが大きな違い

これまで学習してきたことは、一つ一つはちょっとした違いでしかないのですが、大きなプログラムを作っていく上で、このちょっとした違いが便利に活用できる場面があるわけです。

いってみれば、かゆいところに手が届く仕様にするために、ユーザーの声を聞きながら、より便利に使えるルールを決めて変更を加えているということです。

今、実装されているメソッドや関数なども、Pythonがリリースされた当初にはなかった機能かもしれませんし、これから新しい機能が実装されていくでしょう。

これまで学習したことも、自分の中で最新の情報にアップデートしていくことでより便利にPythonを使えるようになると思うので、アンテナを高くして、Python学習を続けていきましょう!

それでは、明日もGood Python!