LoginSignup
2
2

More than 1 year has passed since last update.

Pythonのグローバルスコープについての注意点

Posted at

Pythonのグローバルスコープについてハマった点を紹介します。

ハマった点

以下のコードを実行すると「1」が表示されることを期待したのですが、実際には UnboundLocalError: local variable 'score' referenced before assignment というエラーが表示されます。

score.py
score = 0 # グローバルスコープ

def my_func(point):
  score += point
  return score

if __name__ == "__main__":
  print(my_func(1))

エラーの内容としては「ローカル変数 score を初期化せずに使わっていますよ」という意味なのですが、グローバルスコープの score に引数の point を加算したつもりだったので、一瞬このエラーメッセージの意味が分かりませんでした。

調べてみるとわかったのですが、Pythonには「関数内で変数の値を変更する場合、ローカルスコープが適用される」という注意点があるそうです。

具体的に言うと、「my_func 関数の中で score に point を加算しようとすると、my_func関数内のローカルスコープ内に score という別の変数が作られて、そちら(ローカルスコープ)の変数が使われる」ということになります。

結果的に、「グローバルスコープ score を参照していたつもりが、my_func 関数内のローカルスコープ score が適応されており、ローカルスコープ score を初期化せずに point を加算していたため、上記のエラーが出てしまった」ということが起きていたようです。

解決策

globalで定義することでグローバルスコープの score を my_func 関数内で変更できるようになります。

score.py
score = 0

def my_func(point):
  global score
  score += point # グローバルスコープ score に加算
  return score

if __name__ == "__main__":
  print(my_func(1)) # 1

globals()とlocals()

グローバルスコープやローカルスコープは、globals()locals()で確認することができます。
実際にコードに埋め込んでみたいと思います。

score.py
score = 0

print("check1")
print(globals())

def my_func(point):
  score = 99 # ローカルスコープの score が作成される

  print("check2")
  print(globals())
  print("check2")
  print(locals())

  score += point
  return score

if __name__ == "__main__":
  print(my_func(1))

実行結果は以下の通りです。

実行結果
python scope.py
check1
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x01E1DB70>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'scope.py', '__cached__': None, 'score': 0}
check2
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x01E1DB70>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'scope.py', '__cached__': None, 'score': 0, 'my_func': <function my_func at 0x01DF18E8>}
check3
{'point': 1, 'score': 99}
100

注目すべきは、check1~check3の{'score'}の値です。
check1の「0」はグローバルスコープ score の値です。(=0で初期化している)
check2の「0」はグローバルスコープ score の値です。(=99されたのはローカルスコープの方)
check3の「99」は、score = 99 をしたことにより作成されてしまった、ローカルスコープ score の値です。
確かに、ローカルスコープ score が作成されたことが確認できるかと思います。

次に、global定義するとどうなるかを確認してみます。

score.py
score = 0

print("check1")
print(globals())

def my_func(point):
  global score

  print("check2")
  print(globals())
  print("check3")
  print(locals())

  score += point
  return score

if __name__ == "__main__":
  print(my_func(1))

実行結果は以下の通りです。

実行結果
check1
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x01B2DB70>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'scope.py', '__cached__': None, 'score': 0}
check2
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x01B2DB70>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'scope.py', '__cached__': None, 'score': 0, 'my_func': <function my_func at 0x01B018E8>}
check3
{'point': 1}
1

check1の「0」はグローバルスコープ score の値です。(=0で初期化している)
check2の「0」はグローバルスコープ score の値です。
check3には、ローカルスコープ score が登場しません。なぜなら score = 99 を削除したことにより、ローカルスコープ score が作成されなくなったためです。

global定義をすると、きちんとグローバルスコープ score を扱えることが確認できると思います。

以上です。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2