Python Global変数の使い方と挙動

Python

Python の Global 変数(の使い方と挙動について,纏めてみた.

グローバル変数の使い方

そもそもグローバル変数とは,プログラム内にて複数の関数が共通して使うことのできる変数.

あまりよく分からないという方は,挙動をみてからの方が理解しやすいでしょう.

関数の外側で定義したものはグローバル変数となり,関数内からグローバル変数にアクセスする場合は「これはグローバル変数だ」と宣言します.

グローバル変数は以下のように宣言します.

global 変数名

関数の外側にて,グローバル変数 a を宣言し,関数 add_func 内からアクセスする場合は以下のようになります.

a=0 #グローバル変数
def add_func(x):
    global a
    a+=x

add_func は x を引数に取り,a に x を足し合わせる関数です.

x=4 として関数を呼び出す前と後での a の変化を見てみます.

a=0
def add_func(x):
    global a
    a+=x
print(a) # 0
add_func(4)
print(a) # 4

a は最初 0 で初期化しているので,最初の print 文では 0 が出力されます.

その後 add_func(4) で 4 を足す操作をすることで,2回目の print 文では 4 が出力されます.

まあ,基本的にはこれだけではあるのですが,ここからは色々な挙動を見て性質を理解してみます.

関数内からの参照とローカル変数との違い

変数を操作するには,グローバル宣言が必要ですが,参照するだけなら特に宣言は必要ありません.

b = 134
def print_b():
    print(b)

print_b() #134

print_b() は単純に b という名前の変数をプリントするという関数ですが,グローバル変数 b がプリントされます.

グローバル変数 b は 134 で初期化されてるので,関数 print_b() を呼び出すと 134 がプリントされます.

同様な,少し違う例だと..

b = 134
def calc_c():
    c = b +16
    print(c)

calc_c() # 150
print(c) # NameError:name 'c' is not defined

calc_c() は関数内でローカル変数 c を定義して,それをプリントする関数です.

ローカル変数 c は,b という名前の何かしらの変数を参照し,それに 16 を足した値で定義されます.

ここでは,グローバル変数 b の 134 が参照され,それに 16 を足した 150 が ローカル変数 c となり,プリントされます.

c はローカル変数なので,当然,プリントしようとしても未定義だというエラーが出ます.

では,関数内でグローバル変数と同じ名前のローカル変数を定義するとどうなるでしょうか.

b = 134
def print_b():
    b = 1566
    print(b)

print_b() #1566
print(b) #134

print_b() は,関数内で,ローカル変数 b を定義して,b という変数をプリントする関数です.

print_b() を呼ぶと,「1566」 がプリントされます.

これは,同じ名前の グローバル変数 b が元々「134」と定義されていますが,ローカル変数 b の方が参照されてプリントされているということです.

関数外で print(b) とすると,グローバル変数 b が参照され,グローバル変数 b には何も操作が加えられていないので,初期化した値の「134」がプリントされます.

では,このようにするとどうでしょうか.先の関数 print_b() 内の 1 行目と 2 行目を入れ替えただけの場合です.

b =134
def print_b():
    print(b) # Error箇所
    b = 1566

print_b() # UnboundLocalError: local variable 'b' referenced before assignment
print(b)

print_b() という関数は,まずグローバル変数 b をプリントしたのち,ローカル変数 b を「1566」で初期化する,と読めます.

このように,同じ名前のグローバル変数とローカル変数が混在する関数を呼ぶとエラーが出てしまいます.

「 local variable ‘b’ referenced before assignment 」,「ローカル変数 b が定義される前に参照されている」ということ.

エラーになる理由は以下のような感じ.

関数 print_b() が定義された時点で「b = 1566」という一行があることで, print_b() 内では「b という変数はローカル変数として扱う」とされるのです.

そして,print_b() を呼び出すと,まず最初に print(b) つまり,「ローカル変数 b をプリントしてくれ」という指示がきます.

変数 b は,確かにこの変数内では「ローカル変数として扱われる」ことだけは決定していますが,まだその詳細は定義されていません.

なので,それをプリントしろという指示に対しては「ローカル変数 b が定義される前に参照されている」というエラーが吐かれるのです.

あまり内部処理に詳しいわけではないのですが,「b = 1566」という一行のようにローカル変数たらしめる操作があることで,関数が定義された時点で,それはローカル変数となるみたいですね.

逆に関数内で一度でもグローバル宣言を行えば,その関数内では全てグローバル変数として扱われます.

例をあげると

u=0
def plus(x):
    if x==1:
        global u
        u+=1
    elif x==2:
        u+=2
    else:
        print(u)

グローバル変数 u に,引数 x が 1 か 2 なら,1 または 2 を足す,それ以外の引数の時は 変数 u をプリント,という関数 plus(x)

一見,一つ目の条件分岐のみでしかグローバル宣言されていないように見えますが,関数全体でグローバル宣言されていることになります.

この関数を定義した上で,plus(2),plus(5) と順に呼び出すと,

plus(2)
plus(5) # 2

print(u) # 2

という結果に.plus(2) でグローバル変数 u = 0 に 2 が足され,plus(5) でグローバル変数 u がプリントされます.

もう一度確認のために,print(u) をすると,グローバル変数 u にしっかりと 2 が足される操作がされていたことがわかります.

一見,グローバル宣言がされている一つ目の条件分岐を通らなくとも,u は関数内でグローバル変数として扱われていることになります.

では,今度は,2番目の条件分岐でグローバル宣言するとどうなるか.

u=0
def plus(x):
    if x==1:
        u+=1
    elif x==2:
        global u
        u+=2

SyntaxError: name 'u' is assigned to before global declaration

これは関数の定義時にエラーが出ます.「name ‘u’ is assigned to before global declaration」,つまり,「変数 u がグローバル宣言される前に,(ローカル変数っぽく) 割り当てられてますよ」といったエラー.

詳しい内部処理はわかりませんが,plus() という関数定義時にまず,上から順に読んでるのでしょう,

その際まず,「u+=1」という行が読まれます.これにより,「u はローカル変数っぽいな」という判断がなされます.

その後,「global u」という一行により,「あれ?ローカルじゃなかったん?」というエラーが「name ‘u’ is assigned to before global declaration」なんだと思います.

先の,一つ目の条件分岐にグローバル宣言があった例では,関数定義時にコンパイラが上から順に読み,「global u」があったその時点で,条件分岐とかも関係なしに,その関数内では「uはグローバル変数だ」という処理になるのだと思います.

つまり,条件分岐を入れようとも,関数内で同じ名前のローカル変数とグローバル変数は共存出来ないのです.

まとめ

基本的なグローバル変数の使い方を紹介しました.

その後挙動に関して見てみました.

関数内で同じ名前のローカル変数とグローバル変数は共存出来ないということをみました.

この判断基準は,関数を定義時に上から一行ずつ読み,ローカル変数たらしめる操作が先にあった場合,その関数内ではローカル変数に.

グローバル宣言が先にあった場合は,その関数内ではグローバル変数として扱われるのです.

拙い,かつ,当たり前のようなことを纏めてみましたが,自分自身はグローバル変数をより理解する機会となりました.

コメント

タイトルとURLをコピーしました