Works by

Ren's blog

@rennnosuke_rk 技術ブログです

【python】pythonにおけるスコープ【グローバル変数】

pythonでは変数のスコープに最新の注意を払う必要があり、特に万一グローバル変数(厳密にはスコープはモジュール内に収まるので、モジュール変数?)の変更を扱うような場合、気を緩めていると下のコードに示すような問題を引き起こす可能性があります。

is_odd = False
def check_odd(value):
    if value % 2 == 1:
        # 書き換え(たつもり)
        is_odd = True

check_odd(1)
print(is_odd) # False(Trueではない)

のような問題が起こります。 問題はランタイムがこれを正常なコードとして解釈できてしまうことで(当然ですが)、check_odd内でのグローバル変数is_oddの書き換え(たつもり)処理は単なるcheck_odd中のローカル変数is_odd定義に終わります。なのでグローバル変数is_oddに変化はありません。

global修飾子を使えば、一応所望の結果を得ることができます。

is_odd = False
def check_odd(value):
    global is_odd # グローバルなis_oddを参照
    if value % 2 == 1:
        # グローバルなis_oddを書き換え
        is_odd = True

check_odd(1)
print(is_odd) # True

ただ身も蓋もないことを言ってしまうと、個人的にはこのようなグローバル領域に変数を置くこと自体避けるべきと考えます。上記のような副作用を防ぐ意図だけでなく、変数=状態の変遷という意味でも、特別なことがない限り変数はオブジェクトに管理させるべきです。

グローバル領域には、基本的に定数扱いの変数のみを置くべきだと思います。

# ホントはmathやnumpyのπやexpの使用を推奨
PI = 3.141592
NAPIER = 2.718281828459045

def gaussian(x, mu, sigma):
    return 1 / ((2 * PI) ** 0.5 * sigma) * (NAPIER ** ( - (x - mu) ** 2 / (2 * sigma * sigma)))

print(sum([gaussian(i, 0, 1) for i in range(-100,100)])) #1.000000109372639

前述の通り、pythonではglobal修飾子を明示的につけない限り、変数への代入記述はローカル変数に対するものと解釈されます。なのでglobalと書かなければ故意にグローバル変数を書き換えてしまうことはありません。仮にグローバル変数=定数という規約のもと、グローバル変数と同じ名前の変数への代入をより小さなスコープ内で行った場合、それはそのスコープ内での変数定義となります。

ちなみに変数の参照時には、最も内側のスコープから外側へと同名の変数を探索します(それでもなかったら、Build-Inの変数を参照します)。すなわち同一スコープ内に定義がされてない場合、勝手に外側のスコープ内の変数が参照されます。そのためグローバル変数は参照するだけならglobal修飾子を必要としません。