この記事
nonlocal
ってどこに使うだろうって思ってたけど、ここだったのね、って話。
クロージャ作成例
# クロージャを用いた関数生成関数
def MakeFunc(a):
""" a : 作成する関数に指定するパラメーター
"""
def Func(b):
""" b : 作成された関数実行時の引数
"""
nonlocal a # バグ回避のためのおまじない
return a*b
return Func
# クロージャ使用
F2 = MakeFunc(2)
print(F2(3))
pythonでクロージャを使う場合の注意点
- 関数内で代入が行われた変数は、global文またはnonlocal文で宣言が無い限り、その関数内のローカル変数となる
- よって、上位関数のスコープにあり、クロージャに用いる変数は、上記の例のように
nonlocal
文で宣言しておくことで、意図せずにバグを生じることが防げる。 -
nonlocal
文で宣言することで、クロージャであることが明確になるので、不要な場合にもそうした方が良い。
※上記例ではnonlocal文が無くても問題無い
確認例
以下は、この辺の動作をJupyter上で確認したものです。
例1:バグで実行時エラーとなる書き方
In [1]
def MakeClosure(a):
def Func(b):
print(a)
a = a + b
print(a,b)
return Func
In [2]
F1 = MakeClosure(1)
F1(2)
--------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
Input In [2], in <cell line: 2>()---> 1 F1(2)
Input In [1], in MakeClosure.<locals>.Func(b) 2 def Func(b):
---> 3 print(a) 4 a = a+ b 5 print(a,b)
UnboundLocalError: local variable 'a' referenced before assignment
例2:別の変数を用いて例1を修正
In [4]
def MakeClosure2(a):
def Func(b):
print(a)
c = a+ b
print(c,b)
return Func
In [5]
G1 = MakeClosure2(1)
In [6]
G1(2)
1
3 2
In [7]
G9 = MakeClosure2(9)
In [8]
G9(2)
c
9
11 2
例3:global
を用いて例1を修正し、実行時エラー
In [9]
def MakeClosure3(a):
def Func(b):
global a
print(a)
a = a+ b
print(a,b)
return Func
In [10]
H1 = MakeClosure3(1)
In [11]
H1(2)
Traceback (most recent call last)
Input In [11], in <cell line: 1>()---> 1 H1(2)
Input In [9], in MakeClosure3.<locals>.Func(b) 2 def Func(b): 3 global a
---> 4 print(a) 5 a = a+ b 6 print(a,b)
NameError: name 'a' is not defined
例4:nonlocal
を用いて例1を修正し
In [12]
def MakeClosure4(a):
def Func(b):
nonlocal a
print(a)
a = a+ b
print(a,b)
return Func
In [13]
I1 = MakeClosure4(1)
In [14]
I1(2)
1
3 2