関数をネストする
Python では、関数をネストさせて定義することができます。
code: 0108_function_nest.py
def squre(number):
def multiple_me(number):
return number * number
return multiple_me(number)
print(squre(3))
関数squre() の内部で定義した関数 multiple_me() は、関数 squre() からしかアクセスできません。
では、次の例です。
code: 0109_access_nested_function.py
msg='Bonjour Python'
def greeting(name):
msg = ''
def say_hello():
msg += f'Hello {name} '
print(msg)
return say_hello
action = greeting('Python')
action()
action()
print(msg)
code: bash
$ python 0109_access_nested_function.py
Traceback (most recent call last):
File "0109_access_nested_function.py", line 9, in <module>
print(action())
File "0109_access_nested_function.py", line 4, in say_hello
msg += f'Hello {name}'
UnboundLocalError: local variable 'msg' referenced before assignment
この例では、関数greeting() が返すのは、内側で定義されている関数say_hello()のオブジェクトIDです。つまり、外側の関数は内側の関数をそのまま返しています。
このとき、内側の関数 say_hello() が、外側の関数 greeting() で定義している変数 name にアクセスしています。
内側の関数はその外側の関数で定義されている変数を読み取ることはできても、書き込むことができません。そのため msg は同じ変数名であっても名前空間が違うため、say_hello() で書き込もうとしたmsg は、設定される前に参照(+= の演算)したためエラーになってしまっています。
こうしたときは、次のようにnonlocal 変数名を宣言する必要があります。
code: 0110_function_nest_with_nonlocal.py
msg='Bonjour Python'
def greeting(name):
msg = ''
def say_hello():
nonlocal msg
msg += f'Hello {name} '
print(msg)
return say_hello
action = greeting('Python')
action()
action()
print(msg)
この例では、変数msg は say_hello() のローカル変数ではない、つまり外側の関数で定義しているローカル変数だと宣言しているわけです。
code: bash
$ python 0110_function_nest_with_nonlocal.py
Hello Python
Hello Python Hello Python
Bonjour Python
関数greetig() は状態として msg を保持していて、内側の関数がaction() として実行されるたびにその内容が変わっていくことがわかります。
nonlocal はひとつ外側の関数で定義されたローカル変数を参照する宣言ですが、
global ではどうなるか見てみましょう。
code: 0111_function_nest_with_global.py
msg='Bonjour Python'
def greeting(name):
msg = ''
def say_hello():
global msg
msg += f'Hello {name} '
print(msg)
return say_hello
action = greeting('Python')
action()
action()
print(msg)
global はこのファイルで定義している変数だとする宣言で、こうした変数はグローバル変数と呼ばれます。
code: bash
$ python 0111_function_nest_with_global.py
Bonjour Python Hello Python
Bonjour Python Hello Python Hello Python
Bonjour Python Hello Python Hello Python
このようなネストされた関数で、外側の関数をエンクロージャー(Enclosure) 、内側にある関数をクロージャー(Closure) と呼びます。
Pythonの関数は第一級関数 (ファーストクラス関数(first-class function)とも言う) となります。 これは、次のことを表した言葉です。
関数を変数にセットできる
関数をリストや辞書、その他のデータとして保持できる
関数を他の関数に渡すことができる
他の関数を返す関数を定義できる
ファーストクラス関数やクロージャーなどの言葉が大事なのではなくて、それが意味する内容が大事だということを忘れないでください。