Pythonのglobal宣言について
Pythonを人に教えていた時のこと、グローバル変数を自作関数の中で参照する機会がありました。私はグローバル変数を自作関数の中から書き込むようなコードを書かないのでその宣言が存在することすら知りませんでした。
その時にglobal宣言についての挙動を調べたのでまとめておきます。
グローバル変数とは
グローバル変数とは、プログラムのすべてで参照できる変数のことです。Pythonで書くと以下のようなものです。
# 変数を宣言と定義
testVar = "hello world"
# 自作関数の定義
def testFunc():
print(testVar)
# 自作関数を呼び出し
testFunc()
この例の中では`testVar`がグローバル変数になっています。
しかし、このようにtestVarを関数内で定義するとその関数の外からは操作することができなくなります。これをグローバル変数に対してローカル変数と呼びます。
# 変数を宣言と定義
testVar = "hello world"
# 自作関数の定義
def testFunc():
testVar2 = "Hello World"
print(testVar)
# !! 呼び出せない !!
print(testVar2)
# 自作関数を呼び出し
testFunc()
上の例では、`testVar2`は`testFunc()`の中のローカル変数となっています。そのため、外では扱うことができません。
今回はこのグローバル変数の挙動について考えていきます。
global宣言
global宣言とは、グローバル変数を関数内から書き込みできるようにするための宣言です。(読み込みは書かなくても大丈夫です)
# 変数を宣言と定義
testVar = "hello world"
# 自作関数の定義
def testFunc():
global testVar
testVar = "書き換えられました"
# 自作関数を呼び出し
testFunc()
print(testVar)
`testFunc`の中では`testVar`を"書き換えられました"に書き換えています。これを書かないと、`testVar`はtestFuncのローカル変数として判断されます。
# 変数を宣言と定義
testVar = "hello world"
# 自作関数の定義
def testFunc():
testVar = "書き換えられました" # testFuncの中の新しい`testVar`が宣言された
# 自作関数を呼び出し
testFunc()
print(testVar)
もう一度言います。
グローバル変数を関数内で書き換えようとすると、ローカル変数の宣言として解釈されるのです。
???
私が疑問に思ったところはこの部分です。そもそもグローバル変数と同名のローカル変数が作れるのか? と思いました。
個人的にはそんな設計はしない方がいいと考えていますが、考察してみましょう。
C言語でのグローバル変数
結論から言うと、同名のローカル変数は作れました。
#include<stdio.h>
int testVar = 0;
int main(void){
int testVar = 1;
printf("%d\n", testVar);
return 0;
}
この例ではグローバル変数のtestVarとグローバル変数のtestVarが作成されています。`1`が出力されるのでそれがわかると思います。
書き込みも問題なくできます。
#include<stdio.h>
int testVar = 0;
int main(void){
testVar = 1;
printf("%d\n", testVar);
return 0;
}
ではなぜPythonではglobal宣言なんてものがあるのでしょうか?
Pythonでのglobal宣言の必要性
Pythonでは変数の宣言と定義が同じ文で行われます。
testVar = 1
これではグローバル変数を書き込んでいるのか、ローカル変数を宣言して定義しているのか判別できません。
そのため、Pythonでは書き込みの際にglobal宣言が必要になるのです。
蛇足: じゃあ明確に定義じゃなければいいのか?
以下のコードを作りました。
# 変数を宣言と定義
testVar = 0
# 自作関数の定義
def testFunc():
testVar += 1
# 自作関数を呼び出し
testFunc()
print(testVar)
`testVar += 1`はtestVarが宣言されていない時には使えないので、これではローカル変数の宣言にはなりません。
結果から言うとエラーが出ました。
UnboundLocalError: cannot access local variable 'testVar' where it is not associated with a value
この場合はローカル変数として解釈するようです。
結論
色々考察しましたが、以下のルールでいいようです。
グローバル変数をローカルで読み込む時: そのまま使える
グローバル変数をローカルで書き込む時: global宣言が使える
まあ色々書きましたが、この辺のことを考えなくていいように同名のローカル変数は作らず、クラスなどを使ってスコープを明示しながら作るほうが安全だと思います。