TL;DR
プログラミングで必ず出てくる言葉の一つに「スコープ」がある。スコープは参照できる範囲、直接アクセス可能な領域のことを言う。一言でいうと影響の範囲である。今回は Python のスコープについて解説する。
ローカルスコープとグローバルスコープのポイント
- 関数内で変数を定義すると、その関数内のローカルスコープ内に定義される
- 関数の引数や変数はローカル変数なので外からは参照できない
- グローバルスコープは、モジュールの一番上にあるスコープを指す
- 関数内でグローバル変数を参照したあと、その関数内で同名のローカル変数を定義できない
- global 文をつけると関数内でグローバル変数の値を書き換えることができる
スコープと似ているものとして思い浮かぶのが名前空間だが、スコープと名前空間は違う。名前空間は名前の衝突を避ける為にあるが、スコープは参照できる範囲のことを言う。変数を用意すると、その変数はコードの記述位置の一番狭い範囲に定義される。
スコープは大きく分けると 2 つある。ローカルスコープとグローバルスコープである。
ローカルスコープ
例えば関数内で変数を定義すると、その関数内のローカルスコープ内に定義される。関数の引数や変数はローカル変数なので外からは参照できない。
グローバルスコープ
Python のグローバルスコープは、モジュールの一番上にあるスコープを指す。モジュールのトップレベルで変数を定義すると、モジュールのグローバルスコープ内に定義される。
Python において、関数内でグローバル変数を参照したあと、その関数内で同名のローカル変数を定義できないようになっている。
g = 'global python'
def f():
print(g)
g = 'local python'
print(g)
f()
# UnboundLocalError エラーになる
globals()
変数の一覧を返す組み込み関数に、globals()と locals() がある。グローバル変数の一覧は、組み込み関数 globals() を使うと取得可能である。
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}
>>> x = 'python'
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'x': 'python'}
一方、locals()というのがある。これはローカル領域の変数の値を全て返す。
global 文をつけると関数内でグローバル変数の値を書き換えることができる。これは、ローカルに記述した変数がローカル変数でないことを CPU に伝えている。したがって、この宣言によりローカル内でもグローバル変数の値を変更できるようになる。
g = 'global python'
def f():
global g
print(g)
g = 'local python'
print(g)
f()
# global python
# local python
とはいえ、コードの保守性を下げる原因になるので、注意すべきである。
尚、グローバル変数がコンテナオブジェクト(リストや辞書)の場合は global 文は不要である。なぜなら、コンテナオブジェクトの代入時には参照が先に行われた後に値の更新が行われるためである。
nonlocal
関数が入れ子になっている場合はどうすればいいのか?入れ子となった関数内で外側のスコープの値を書き換えたい場合は、nonlocal 文で宣言するとよい。
nonlocal 文 なし
def func():
w = 'w'
def in_func():
w = 1
print(w)
in_func()
print(w)
func()
# 1
# w
nonlocal 文 あり
def func():
w = 'w'
def in_func():
nonlocal w
w = 1
print(w)
in_func()
print(w)
func()
# 1
# 1