今回は、クロージャについて紹介します。
クロージャは、オブジェクト指向の特徴を利用しています。
クロージャとは
クロージャには、以下の特徴があります。
- 関数内関数
- 関数の戻り値に関数内関数を定義
- 外側の関数の状態を持った関数
実際のコードを見て解説します。
def outer(a, b):
def inner():
c = a + b
return c
return inner
f = outer(1, 2)
print(f())
クロージャの特徴① 関数内関数
関数outer()
の内で、関数inner()
が定義されています。
このinner()
関数のように、関数内で定義されている関数を関数内関数
といいます。
クロージャの特徴② 外側の関数の戻り値に関数内関数を設定
外側の関数の戻り値(return文)に関数内関数
を設定します。
今回の例では、inner
をreturn文
に設定しています。
このときのinner
関数には()
をつけないことに注意してください!
戻り値には、inner
関数のオブジェクトをセットしたいためです。
クロージャの特徴③ 外側の関数の状態を持った関数
3つ目のクロージャの特徴は、今までの2つの特徴よりも少し難しいです。
外側の関数の状態を持つとは、簡単にいうと、
外側の関数が呼ばれたときの、引数の情報を保持するということです。
これだけでは、わかりにくいと思うので、次章にてクロージャの動きと合わせて説明します。
クロージャの動き
以下のコードは、円の面積を求めるクロージャを実装したコードです。
外側の関数がcalc_area
で、クロージャがcalc
関数にあたります。
外側の関数calc_area
は、円周率pi
を引数とし、
クロージャのcalc
関数を戻り値としています。
一方で、クロージャcalc
関数は、円の半径radius
を引数として、
円の面積を計算した結果を戻り値にしています。
result1〜3
の変数には、それぞれの円周率で円の面積を求めるオブジェクトが格納されています。オブジェクトというと難しいかもしれませんが、
それぞれの円周率ごとに円の面積を求めることができる箱ができたイメージです。
あとは、半径を引数として指定すれば、それぞれの円周率で円の面積が求められます。
def calc_area(pi):
def calc(radius):
area = radius * radius * pi # 円の面積=半径x半径x円周率
return area
return calc
result1 = calc_area(3.14159) # pi = 3.14159
result2 = calc_area(3.14) # pi = 3.14
result3 = calc_area(3) # pi = 3
radius = 10 # 半径 10とする
print(result1(radius)) # radius = 10, pi = 3.14159 のときの円の面積
print(result2(radius)) # radius = 10, pi = 3.14 のときの円の面積
print(result3(radius)) # radius = 10, pi = 3 のときの円の面積
実行結果
314.159
314.0
300
それぞれの円周率で、円の面積を求めた計算結果が表示されました。
クロージャのメリット
クロージャの使い方がわかったところで、なぜクロージャを使うのか気になると思います。
なので、クロージャのメリットについて、紹介します。
クロージャのメリットは、状態を保持したまま、違う処理を実行できる点です。
上で紹介したコードをみてください。
result1〜3
で外側の関数calc_area
に対し、
それぞれの円周率pi
を引数とした実行結果を保持しています。
この実行結果を保持したresult1〜3
に対し、
半径radius
を指定して、円の面積を求めることができます。
半径radius
は、サンプルでは、10
を指定していますが、変更も可能です。
このようなクロージャのメリットが、
クロージャの3つ目の特徴「外側の関数の状態を持った関数」に相当します。
まとめ
クロージャは、理解がしづらいところもありますが、
マスターすれば、コードのレベルが上がります。
ぜひクロージャをマスターしていきましょう!