ネストされた関数とは?
関数はPythonの「第一級市民」の1つです。つまり、関数は整数、文字列、モジュールなどの他のPythonオブジェクトと同じレベルにあるということです。関数は動的に生成・破棄でき、他の関数に渡したり、値として返したりすることができます。
Pythonは「ネストされた関数」または「内部関数」という概念をサポートしており、これは単に別の関数の内部で定義された関数のことです。この記事の残りの部分では、「内部関数」と「ネストされた関数」という言葉を同じように使うことにします。
なぜ別の関数の中に関数を作りたいのか、その理由はさまざまです。内部関数は、そのスコープ内の変数にアクセスすることができます。この記事では、Pythonの内部関数のさまざまな側面を探ります。
内部関数の定義
Pythonで内部関数を定義するには、Pythonの def
キーワードを使って、他の関数の中に関数を作成します。以下はその例です。
def function1(): # outer function
print ("Hello from outer function")
def function2(): # inner function
print ("Hello from inner function")
function2()
function1()
出力
Hello from outer function
Hello from inner function
上記の例では、function2()
が function1()
の内部で定義されており、内部関数になっています。function2()を呼び出すには、まず
function1()を呼び出さなければなりません。そして、
function1()は、その内部で定義された
function2()` を呼び出すようになります。
ここで重要なのは、内側の関数が実行されるためには、外側の関数が呼び出されなければならないということです。もし、外側関数が呼ばれなければ、内側関数は決して実行されません。このことを示すために、上記のコードを以下のように変更して実行してみてください。
def function1(): # outer function
print ("Hello from outer function")
def function2(): # inner function
print ("Hello from inner function")
function2()
このコードは実行されても何も返しません!
もう一つの例を挙げよう。
def num1(x):
def num2(y):
return x * y
return num2
res = num1(10)
print(res(5))
出力
50
このコードは、2つの数字、つまり 10 と 5 の掛け算を返す。この例では、内側の関数が外側の関数でアクセスできる変数にアクセスできることを示しています。
ここまでで、内部関数の内部で外部関数の変数にアクセスできることがわかりました。では、内側関数の内部から外側関数の変数を変更しようとするとどうなるでしょうか?どうなるか見てみましょう。
def function1(): # outer function
x = 2 # A variable defined within the outer function
def function2(a): # inner function
# Let's define a new variable within the inner function
# rather than changing the value of x of the outer function
x = 6
print (a+x)
print (x) # to display the value of x of the outer function
function2(3)
function1()
出力
出力
出力は、外側の関数で定義された変数の値を内側の関数から表示することは可能ですが、変更することはできないことを示しています。x = 6というステートメントは、外側の関数
function1()で定義された変数
xの値を変更するのではなく、内側の関数
function2()の中で新しい変数
x` を作成するのに役立ちました。
次のセクションでは、Python で内部関数を使用する主な理由について説明します。
なぜInner Functionsを使うのか?
カプセル化
関数を内部関数として作成することで、関数の外側で起こっていることすべてから関数を保護することができます。この場合、関数はグローバルスコープから隠されます。以下はその例です。
2
9
出力
def outer_function(x):
# Hidden from the outer code
def inner_increment(x):
return x + 2
y = inner_increment(x)
print(x, y)
inner_increment(5)
#outer_function(5)
上記のコードでは、inner_increment()
関数を呼び出そうとしていますが、代わりにエラーが発生しました。
そこで、以下のように inner_increment()
の呼び出しをコメントアウトし、 outer_function()
の呼び出しをアンコメントしてください。
Traceback (most recent call last):
File "C:/Users/admin/inner.py", line 7, in <module
inner_increment(5)
NameError: name 'inner_increment' is not defined
出力
def outer_function(x):
# Hidden from the outer code
def inner_increment(x):
return x + 2
y = inner_increment(x)
print(x, y)
#inner_increment(5)
outer_function(5)
上のスクリプトは、内側の関数、つまり inner_increment()
は、外側の関数に渡されたパラメータ x
の値によって inner_increment
関数内の変数 x
が影響を受けないので、その外側で起こっていることから保護されていることを示しています。言い換えれば、内部関数内の変数は外部からはアクセスできない。このようなデザインパターンには大きな利点があります。外側の関数ですべての引数をチェックした後、内側の関数でのエラーチェックを安全に省略することができるのです。
クロージャとファクトリー関数
今まで見てきた例では、普通の関数が他の関数の中にネストされているだけでした。このような関数は、他の関数の中にネストさせるのではなく、別の方法で書くことも可能です。なぜネストしなければならないのか、その理由は特にない。
しかし、クロージャの場合は、ネストされた関数を使わなければならない。
関数にデータを渡す場合、必ずしもパラメータでデータを渡さなくとも、関数にデータを束縛したり渡したりすることができます。これは、クロージャを使うことで実現する。クロージャとは、メモリ上にない値でも、それを包含するスコープにある値を記憶することができる関数オブジェクトのことである。つまり、ネストされた関数がその内包するスコープにある値を参照するときに、クロージャを持つことになる。
クロージャの目的は、内部関数が呼び出されたときに、それがメモリ上になくても、その環境の状態を記憶させることである。クロージャは内部関数によって引き起こされますが、内部関数ではありません。クロージャは、スタック上のローカル変数を閉じることで動作し、スタックの作成が実行終了した後も残ります。
Pythonでクロージャを作成するために必要な条件は以下のとおりです。
- ネストされた関数が存在すること
- 内包する関数は、内包するスコープで定義された値を参照する必要があります。
- 内包される関数は、内包される関数を返さなければならない。
次のような例を考えてみましょう。
5 7
出力
def function1(name):
def function2():
print('Hello ' + name)
return function2
func = function1('Nicholas')
func()
上のコードは、クロージャを使うと、関数渡しを介してそのスコープの外から 関数を生成して呼び出すことができることを示している。function2()のスコープは
function1()` の内部にしかありません。しかし、クロージャを使うことで、このスコープを拡張し、スコープの外から呼び出すことができるようになった。
内部関数は、ファクトリー関数を定義するのに役立ちます。ファクトリー関数とは、別のオブジェクトを生成する関数である。たとえば
Hello Nicholas
出力
def power_generator(num):
# Create the inner function
def power_n(power):
return num ** power
return power_n
power_two = power_generator(2)
power_three = power_generator(3)
print(power_two(8))
print(power_three(4))
上のスクリプトでは、power_n(power)
関数から power_two
と power_three
という2つのオブジェクトを生成しています。これは、power_n(power)
がファクトリー関数であることを意味する。なぜなら、power_two
とpower_three
は、我々が渡したパラメータを用いて我々のために関数を生成してくれるからである。
結論
内部関数とは、他の関数の内部で定義された関数のことです。内部関数は、外部関数のスコープ内で定義された変数にアクセスすることはできますが、変 更することはできません。なぜ内部関数を作成する必要があるのか、その理由はいくつかあります。例えば、内部関数はその外側で起こることから保護されています。また、内部関数はPythonでクロージャを作成する良い方法でもあります。
</module