pythonの変数のスコープってどこからどこまで?
新しくプログラミング言語を始めると気になってくるのが変数のスコープです。
どこからどこまでが有効なのか知っていて損をすることはまずありません。
スコープとは
ご存知の方も多いとは思いますが、スコープとは変数の有効範囲の事です。
有効範囲、つまりその変数を利用できる範囲の事です。
pythonのスコープ
早速ですが、次のプログラムを実行すると画面には何が表示されるでしょう?
x = 1
print(x)
はい、当然1
が表示されます。
では次はどうでしょう?
x = 1
def funcX():
x = 2
print(x)
関数funcX
の中でx
に2
を代入しています。
関数を実行していないので当然表示される内容は1
です。
続いて、関数funcX
を実行した場合を考えます。
x = 1
def funcX():
x = 2
funcX()
print(x)
x
を表示させる前にfuncX
を実行しています。
しかし、画面に表示されるのは1
です。
ひょっとしてうまく代入出来なかったのかもしれません、funcX
の中でx
を表示させて確認してみましょう。
x = 1
def funcX():
x = 2
print("funcX : ",x)
funcX()
# funcX : 2
print(x)
# 1
funcX
で表示させたx
にはきちんと2
が入っています。
でもやっぱり、funcX
の外で表示させたx
は1
のままです。
このことから分かるのはx = 1
をした時のx
とfuncX
の中でx = 2
をした時のx
は別物だということです。
同じ変数名でも場所によって中身が違う、それこそがスコープの注意するところです。
グローバルな変数 ローカルな変数
pythonのスコープは基本的にはクラスや関数単位です。
関数の中で定義した変数はその関数の中でしか使えません。
グローバル変数やローカル変数など耳にしたことがあると思います。
それぞれがどんなものなのか簡単に説明します。
グローバル変数
グローバル変数というのは何処からでも参照できる変数の事です。
関数やクラスの外で定義した変数が該当します。
先ほどのプログラムを例に挙げると
x = 1 # <- グローバル変数
def funcX():
x = 2
print("funcX : ", x)
funcX()
print(x) # <- このxはグローバル変数x
となります。
ローカル変数
関数やクラスの中で宣言された利用できる範囲が制限された変数のことをローカル変数といいます。
クラス定義の中で宣言された変数はクラス変数とも呼ばれます。
x = 1 # <- グローバル変数
def funcX():
x = 2 # <-ローカル変数x
print("funcX : ", x) # <- ここのxはローカル変数のx
funcX()
print(x) # <- このxはグローバル変数x
ローカル変数は原則、定義したスコープの外からは呼び出せないので、
x = 1
def funcY():
y = 10
print(x)
# 1
print(y)
# NameError: name 'y' is not defined
となってしまいます。
また、以下のようにグローバル変数に関数の中で代入を行なって変更を加えても、関数の中で宣言された変数扱いになるので、
z = 0
def funcZ():
z = 100
funcZ()
print(z)
# 0
グローバル変数z
の方に影響はありません。
先ほどのx
もその例です。
グローバル変数の参照
先ほど、グローバル変数はどこからでも参照できるという話をしましたが実際にどういうことかと言うと、こう言うことです。
x = 10
def funcY():
y = x
print("y =", y)
funcY()
# y = 10
print("x =", x)
# x = 10
関数funcY
の中ではx
を定義していませんが、y
にはx
の中の値10
が代入されています。
これがグローバル変数は、どこからでも参照できると言うことです。
しかし、次のような場合は注意が必要です。
x = 10
def funcY():
y = x
print("y =", y)
x = 100
funcY()
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# File "<stdin>", line 2, in funcY
# UnboundLocalError: local variable 'x' referenced before assignment
print("x =", x)
# x = 10
ローカル変数x
が代入される前に参照されたと言うエラーが出ました。
プログラムは、基本的に上から順番に実行されるのでfuncY
のy = x
のx
はグローバル変数のx
を指しそうなものですが、そうは問屋が卸しません。
そのあとの行に注目してください。x = 100
を実行しています。
つまり、ローカル変数x
を定義しています。
Pythonは変数を探す時、呼び出されたところを基準としてスコープが小さいところを優先して探します。
スコープが小さいところが優先と言いましたが、自身より小さいスコープの変数は覗きに行きません。
自分を基準として、内側から外側に向かって変数を探しに行きます。
なのでPythonはy = x
の時にfuncY
の中を見渡してx
がないかを探します。
すると、3行目にx = 100
をしているのを見つけます。
なので、1行目のx
はローカル変数のx
として判断されます。
しかし、ローカル変数x
が定義されるのは、y = x
よりも後です。
だから、x
が代入される前に参照されたと言うエラーを吐くわけです。
変数の名前はスコープが小さいところのものが優先的に利用されると言うことを覚えておきましょう。
グローバル変数がどこからでも参照できると言うのは、スコープの小さいところから順番に探して行った時、最後に探される場所がグローバル空間だからと言う理由です。
x = 10
def funcY():
y = x
print("y =", y)
funcY()
# y = 10
print("x =", x)
# x = 10
上の例だと、変数を探しに行く順番が
funcY
の中- グローバル空間
と言う順番になっていてfuncY
のなかにx
がないのでグローバル空間にx
を探しに行きます。
すると、x
が見つかるのでfuncY
のなかのx
はグローバル空間のx
であると認識されるわけです。
グローバル宣言
関数の中でもグローバル変数の値を変更したいときはあると思います。
そんな時に利用できるのが、グローバル宣言です。
関数の中でglobal 変数名
とすることで利用できます。
グローバル宣言をすることで、この関数の中の変数名
はグローバル変数のことであるということを示すことができます。
つまり、関数などの中で加えた変更をグローバル空間の変数に反映させることができます。
x = 0
y = 0
def funcX():
global x
x = 10
y = 100
funcX()
print(x)
# 10 <- `funcX`の中でglobal宣言をしているので`x`に加えた変更が反映されている
print(y)
# y = 0 # <- `funcX`の中で`y`に加えた変更がは反映されない
ただ、この様なグローバル宣言をした変数をプログラムの中で多用すると、いつ どこで どのような 変更が加わったのかわかりにくくなってしまうので使い過ぎには気をつけましょう。
おまけ
どのタイミングでどの変数が使えるのか分からん。
という人は組み込み関数globals
、locals
を使うとわかりやすかもしれません。
globals関数
グローバル空間で利用できる変数を変数名をキー、値をバリューとして持った辞書として返してくれます。
x = 10
def funcY():
y = x
print("y =", y)
print(globals()) # グローバル変数を取得
# {'x': 10, 'funcY': <function funcY at 0x108707378>}
※本当は組み込みの関数などいろいろ表示されますが、見づくなるので省略しています。
locals関数
呼び出されたローカル空間で利用できる変数をglobals関数と同じように変数名をキー、値をバリューとして持った辞書を返してくれます。
x = 10
def funcY():
y = 100
print(locals()) # 呼び出されたローカル空間、つまり`funcY`の中で利用できるローカル変数を取得
funcY()
# {'y': 100}
最後に
変数のスコープが分かっていると、プログラムがぐっと書きやすくなります。
人のコードを読む時にも役に立つので、変数のスコープを気にしながらコードを書いてみましょう。