Pythonの関数、超速入門:特集:Visual Studioで始めるPythonプログラミング(3/3 ページ)
今回はPythonの関数――通常の名前付き関数、ラムダ式で作成する無名関数、反復的に値を返すジェネレーター――について見ていこう。
ジェネレーター
「ジェネレーター」は「何らかの値を連続的に生成する」反復可能なオブジェクトであり、ジェネレーター関数を用いて定義する。ジェネレーターはリストやタプルなどと同様に、反復処理に利用できる(ジェネレーター関数の実際の戻り値は反復可能な「ジェネレーター」オブジェクトになる。反復処理では、ジェネレーター関数の呼び出し後に実際に制御を行うのはジェネレータオブジェクト)。
ジェネレーター関数は通常の関数と同様にdefキーワードを使って定義するが(ジェネレーター式も使えるが今回は取り上げない。次回に取り上げる予定だ)、ジェネレーター関数ではreturn文を使わずにyield文で呼び出し側に値を返すのが大きな違いだ。yield文が実行されると制御も呼び出し側に戻るが、関数とは異なりジェネレーターは実行時の状態を記憶しており、次回にジェネレーターへ制御が移ったときには、続きから実行が継続される。
簡単な例を以下に示す。
>>> def gen(n):
... for num in range(n):
... print("generated")
... yield num
...
>>> for cnt in gen(10):
... print(cnt)
...
generated
0
generated
1
generated
2
…… 省略 ……
ジェネレーター関数genは「引数に指定された値未満の整数列を返す」反復可能なオブジェクトを返し、その後の反復処理はこのオブジェクトを使って行われる。実行結果を見ると、呼び出し側のforループでの数値の出力と、ジェネレーター側でのメッセージの出力が交互に行われていることから、呼び出し側のforループとジェネレーターの間で制御が入れ替わっていることが分かる。
上の例では、有限の整数列を生成したが、無限に値を生成するジェネレーターも記述できる。例として、以下にフィボナッチ数を無限に生成し続けるジェネレーターを示す。
>>> def fibgen():
... n0, n1 = 0, 1
... yield n0 # fib(0)を返送
... yield n1 # fib(1)を返送
... while True:
... n0, n1 = n1, n0 + n1 # フィボナッチ数を計算し(n0 + n1)、以前の値を1つ前の値に
... v = yield n1
... if v == "terminate":
... break
ここではyield文で変数vに値を代入している妙な1行がある。実はジェネレーターを呼び出す側からは、ジェネレーターに対してsendメソッドでメッセージを送信できる。送信された値はyield文の評価結果の形で、次回に制御がジェネレーターに戻ってきたときに渡される。ここでは変数vにその値を代入し、その値を調べてループを乱暴なやり方で終了している。実際には「StopIteration」例外が発生するので、例外処理を行うのが正しい。
このジェネレーターの利用例を以下に示す。
>>> fb = fibgen()
>>> for num in fb:
... if num > 50:
... fb.send("terminate")
... print(num)
...
0
1
1
2
…… 省略 ……
21
34
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
StopIteration
先ほどと異なるのは、for文の前にジェネレーター関数fibgenを呼び出している点だ。これにより、ジェネレーターへの参照を手に入れておき、ここではフィボナッチ数が50より大きくなったところで、そのジェネレーターにsendメソッドでループを終了するように通知している。上の例ではメッセージの送信後、もう一度ループが実行された時点で例外が発生している。
あるいは以下のような方法もある。
>>> fb = fibgen()
>>> for cnt in range(10):
... print(next(fb))
...
0
1
1
2
…… 省略 ……
これを見ると分かるが、関数nextにジェネレーターを渡すことで順々にジェネレーターが生成する値を取得することもできる。
なお、以下のようなコードを書くと、無限ループにハマるので注意しよう。
>>> for num in fibgen():
... print(num)
...
0
1
1
2
3
…… 省略 ……
このような場合には[Interactive]ウィンドウの左上にある[リセット]ボタンをクリックしよう。
今回はPythonの関数とジェネレーターについて見た。次回はジェネレーターを生成するもう1つの方法であるジェネレーター式とそれに関連してリストなどの内包表記などについて見ていく予定だ。
Copyright© Digital Advantage Corp. All Rights Reserved.