タイトルそのままですが、 Python 3 の nonlocal
の利用方法について説明してみたいと思います。
Python の変数スコープは原則「関数」がスコープの切れ目となっており、関数の内部で定義された変数に関数の外部からアクセスすることはできません。
def myfunc():
a = 10
print(a) # => NameError: name 'a' is not defined
一方、関数の内側から関数の外側にある変数にはアクセスすることが可能です。
a = 10
def myfunc():
print(a)
myfunc() # => 10
ただし、関数の内側から外側の変数へのアクセスは基本的に「参照」のみが可能です。値を更新するには nonlocal
宣言をしなくてはなりません。
nonlocal
を使った例はこちら。
def gen_counter():
"""呼び出すごとにカウントを上げるカウンタを生成する"""
# クロージャ _counter で利用する現在のカウント
count = 0
def _counter(reset=False):
# 関数の外にある count を更新したいので nonlocal 宣言をする
nonlocal count
if reset:
count = 0
count += 1
return count
return _counter
# カウンタを使用する
c1 = gen_counter()
# nonlocal のおかげで更新した count の値が保持できることの確認
print(c1()) # => 1
print(c1()) # => 2
print(c1()) # => 3
print(c1(reset=True)) # => 1
print(c1()) # => 2
nonlocal
宣言をすることで、 _counter()
関数の外側にある count
変数を _counter()
の内側で更新することができています。
この例では nonlocal
宣言をしないとむしろ「 UnboundLocalError: local variable 'count' referenced before assignment 」というエラーが出てしまうため実行すること自体できません。
ただし、関数の内側から外側の変数の「代入」はできないということについては 1 点注意が必要です。ミュータブル( mutable )なデータ型の場合は「代入」はできなくてもその中身を変更することはできてしまいます。ミュータブルなデータ型の代表はリストです。
def gen_stack():
"""スタックを生成する"""
stack = []
def _stack(value=False):
if value:
# 変数 stack は関数の外側にあるが変更が可能
stack.append(value)
else:
# 変数 stack は関数の外側にあるが変更が可能
return stack.pop()
return _stack
q = gen_stack()
# スタックに値を入れる
q(3) # stack: [3]
q(5) # stack: [3, 5]
q(7) # stack: [3, 5, 7]
# スタックから値を取り出す
print(q()) # => 7 stack: [3, 5]
print(q()) # => 5 stack: [3]
print(q()) # => 3 stack: []
以上です。
ロジックが複雑になる場合はムリに関数と nonlocal
を使うのではなく、クラスを定義してアトリビュートを使う形にするのが一般的かと思います。そのため、このあたりで大きくハマることはあまり無いかと思いますが、利用する場合には挙動を正しく理解して使わないと思わぬバグが生まれるので注意が必要ですね。