クロージャについて学んでいきましょう。
クロージャは、関数内関数を少し難しくしたもの、というイメージです。
ですので、関数内関数の考え方と比べながらみていくと、分かりやすくイメージできるかと思います。
具体的な例と共に、クロージャについて理解していきましょう。
関数内関数とクロージャの違い
具体例で関数内関数とクロージャの違いを見てみましょう。
コード-関数内関数
def outer(a):
def inner(b):
print(b)
return
return inner(a)
コード-クロージャ
def outer(a):
def inner():
print(a)
return
return inner
違いを比べてみましょう。
関数内関数 - return文で、関数を実行している。また、関数内の関数において、引数を指定している。
クロージャ - return文で、関数を実行していない(かっこをつけていない)。
また、関数内の関数において、引数を指定していない。
関数内関数は、outer関数→innner関数と、関数が順番に実行されていきます。
ちなみに、関数というのは、実行が終わると関数内の変数や引数はメモリから消されるのが一般的です。
しかし、Pythonでは関数の中からでも外の関数が保持している変数を維持することができるのです。
そして、これをクロージャと呼びます。
クロージャがあると何が良いのか?
ここから、クロージャを使うことによるメリットについて整理していきましょう。
結論からお伝えすると、クロージャを使うことによって、ある状態を保持したまま、違う処理を実行することができるようになります。
これも具体例を見ながら順番に理解していくのが良いと思いますので、実際のコードを見ていきましょう。
先程のコードを使います。
コードdef outer(a):
def inner():
print(a)
return
return inner
ここで、このようなコードを書くとどうなるでしょうか?
コードclosure = outer(10)
このコードは、10を引数としてouter関数を実行しています。そして、その結果を変数closureに代入しています。
ここで、outer関数を実行するとどうなるでしょうか?
outer関数は、innerを返していますが、これはinner関数のアドレスを示しています。
このあたりの詳細は、メモリとアドレスという記事や、関数という記事で詳しく説明しています。
つまり、aにはinner関数のブロックが入っているアドレスが入っているのです。
ここで意識したいことは、outer関数を呼び出す時に10という引数を設定したことです。
つまり、aはinner関数のブロックが入っているのと同時に、inner関数は10という引数を覚えているのです。
そして、inner関数を実行するには、closure()とかっこを付けます。
inner関数は引数a(つまり10)を返すよう命令が組まれていますので、結果として10が出力されます。
コードclosure = outer(10)
closure()
closure = outer(5)
closure()
アウトプット10
5
この性質をうまく使うことによって、様々な初期値を設定した上で関数を実行することが可能になります。
具体的なコードで見ていきましょう。
コード1 def pi(a):
2 def calc(radius):
3 print(radius * radius * a)
4 return
5 return calc
6 a = pi(3.141592)
7 b = pi(3.14)
8 c = pi(3)
9 a(5)
10 b(5)
11 c(5)
アウトプット78.5398
78.5
75
円の面積を計算するプログラムですが、さまざまな円周率における計算結果を取得するため、クロージャを使いました。
a = pi(3.141592)といった形で円周率の初期値を様々な場合に設定しておくことで、複数の場合において計算が簡単にできるようになりました。
クロージャのまとめ
(1) ある引数を覚えておくことができる機能がクロージャ
(2) メモリとアドレスの関係を学ぶことで、理解が深まる
クロージャを学んだ後は
クロージャの考え方を使ったもう一歩難易度が高い考えがデコレータです。
詳しくは、デコーレータという記事で説明していますので、ぜひご一読下さい。