Pythonではあるスコープの外側で定義されている変数の値を変更しようとするときには、nonlocal文でその変数にアクセスできるようにする必要がある。その方法や注意点を紹介する。
def make_func0():
greet = 'hello'
def func(to):
print(f'{greet} {to}') # make_func0関数のローカル変数greetを参照
return func
myfunc = make_func0()
myfunc('world') # hello world
def make_func1():
greet = 'hello'
def func(to):
greet = 'goodbye' # func関数のローカル変数greetを定義
print(f'{greet} {to}') # func関数のローカル変数greetを参照
return func
myfunc = make_func1()
myfunc('world') # goodbye world(func関数のローカル変数greetを参照)
def check_var():
greet = 'hello'
def func(to):
greet = 'goodbye'
print(f'{greet} {to}')
func('world')
print(f'greet after calling func: {greet}')
check_var()
# 出力結果:
#goodbye world
#greet after calling func: hello
def make_func2():
greet = 'hello'
def func(to):
nonlocal greet # greetが外側のスコープで定義されていることを宣言
greet = 'goodbye' # ローカル変数ではなく外側のスコープにあるgreetに代入
print(f'{greet} {to}')
return func
myfunc = make_func2()
myfunc('world') # goodbye world(make_func2関数のローカル変数greetを参照)
def check_nonlocal():
greet = 'hello'
print('before def func:', greet)
def func(to):
nonlocal greet
greet = 'goodbye'
print(f'{greet} {to}')
func('world') # 上で定義しているローカル関数funcを呼び出す
print('after call func:', greet) # この関数のローカル変数greetの値を確認
check_nonlocal()
# 出力結果:
#before def func: hello
#goodbye world
#after call func: goodbye
def make_func3():
greet = 'hello'
def func(to):
print(greet) # SyntaxError:func関数のローカル変数greetを宣言前に使用
nonlocal greet
greet = 'goodbye'
print(f'{greet} {to}')
return func
def make_counter():
#cnt = 0
def counter():
nonlocal cnt # SyntaxError:変数cntが定義されていない
cnt += 1
return cnt
return counter
Pythonのnonlocal文とは次のようなものだ。
まずはnonlocal文を使わない例を紹介する。以下のmake_func0関数はローカル関数を作成して、それを戻り値とする。そして、ローカル関数の中ではmake_func0関数のローカル変数を参照している。
def make_func0():
greet = 'hello'
def func(to):
print(f'{greet} {to}') # make_func0関数のローカル変数greetを参照
return func
ローカル関数funcは、make_func0関数のローカル変数であるgreetの値を参照している。このように、内側のスコープから外側のスコープにある変数の値を参照することは自由に行える。以下はその呼び出し例だ。
myfunc = make_func0()
myfunc('world') # hello world
make_func0関数が返してきた関数の内部で、make_func0関数のローカル変数greetの値である'hello'を使って出力が行われているのが分かる。
make_func0関数のローカル変数であるgreetの値を内部のローカル関数から変更したいとする。以下はその例だ。
def make_func1():
greet = 'hello'
def func(to):
greet = 'goodbye' # func関数のローカル変数greetを定義
print(f'{greet} {to}') # func関数のローカル変数greetを参照
return func
myfunc = make_func1()
myfunc('world') # goodbye world(func関数のローカル変数greetを参照)
実行結果を見ると、変数greetの値は'hello'ではなく、'goodbye'になっている。しかし、これはfunc関数内でローカル変数greetが新たに定義されるだけで、make_func1関数のローカル変数greetの値は変更されない。このことを確認するために、make_func1関数を基に以下に示すcheck_var関数を定義して呼び出してみよう。
def check_var():
greet = 'hello'
def func(to):
greet = 'goodbye'
print(f'{greet} {to}')
func('world')
print(f'greet after calling func: {greet}')
check_var関数は内部でローカル関数を定義した後、それを返すのではなく、呼び出してから、ローカル変数greetの値を画面に表示するものだ。実行結果を以下に示す。
check_var()
# 出力結果:
#goodbye world
#greet after calling func: hello
「func('world')」行でローカル関数が呼び出され、(func関数のローカル変数である)greetの値が'goodbye'になるので画面には「goodbye world」と表示されるが、その後のprint関数呼び出しの結果を見ると分かる通り、外側の関数のスコープにある変数greetの値は'hello'のままで変わっていないことが分かる。
そのため、外側のスコープにある変数の値を変更したいときにはnonlocal文で対象の変数がノンローカルであることを宣言する。
def make_func2():
greet = 'hello'
def func(to):
nonlocal greet # greetが外側のスコープで定義されていることを宣言
greet = 'goodbye' # ローカル変数ではなく外側のスコープにあるgreetに代入
print(f'{greet} {to}')
return func
myfunc = make_func2()
myfunc('world') # goodbye world(make_func2関数のローカル変数greetを参照)
nonlocal文はある変数が現在のスコープよりも外側で定義されていることを宣言する。上の例ではmake_func2関数で定義されている変数greetを(変更可能な変数として)参照するためにローカル関数funcの中で「nonlocal greet」文を実行している。これにより、「greet = 'goodbye'」行ではmake_func2関数のローカル変数greetに代入が行われるようになる。実行結果を見るだけだと、「goodbye world」と出力されているので、違いがよく分からないので、先ほどのcheck_var関数と同様なcheck_nonlocal関数を作成して実行してみたものが以下だ。
def check_nonlocal():
greet = 'hello'
print('before def func:', greet)
def func(to):
nonlocal greet
greet = 'goodbye'
print(f'{greet} {to}')
func('world') # 上で定義しているローカル関数funcを呼び出す
print('after call func:', greet) # この関数のローカル変数greetの値を確認
check_nonlocal()
# 出力結果:
#before def func: hello
#goodbye world
#after call func: goodbye
外側のcheck_nonlocal関数では変数greetの値を'hello'にしている。内部で定義しているfunc関数ではnonlocal文でそれを参照できるようにして、その値を'goodbye'に変更している。func関数を定義した後で、それを呼び出すと、変数greetの値が変更されるので、check_nonlocal関数で行われている2つのprint関数呼び出しでは、その値が変わっている。
なお、nonlocal文に記述する変数は、nonlocal文よりも前に使うことはできず、そうしたコードは関数定義時にSyntaxError例外を発生させる。
def make_func3():
greet = 'hello'
def func(to):
print(greet) # SyntaxError:func関数のローカル変数greetを宣言前に使用
nonlocal greet
greet = 'goodbye'
print(f'{greet} {to}')
return func
また、global文では未定義のグローバル変数を「global undefined_var」のように記述できたが、nonlocal文で記述できるのは既に外側のスコープで定義されている変数のみである。未定義の変数をnonlocal文でノンローカル宣言しようとするとSyntaxError例外が発生する。
def make_counter():
#cnt = 0
def counter():
nonlocal cnt # SyntaxError:変数cntが定義されていない
cnt += 1
return cnt
return counter
最後に、nonlocal文ではグローバル変数を指定できないことも覚えておこう。ローカルなスコープでグローバル変数の値を変更したければglobal文を使用すればよい。
Copyright© Digital Advantage Corp. All Rights Reserved.