005.Pythonのスコープ
初回:2018/09/05
0.今回のまとめ
Pythonのスコープは、複雑怪奇で,曖昧です。きちんとルールを決めて
パターン化しておかないと、面倒なことになります。
1.スコープと変数定義
私にとって、Pythonの一番嫌いなところは、変数宣言とスコープです。
「Pythonは変数のスコープが曖昧な事を忘れない」という記事を一度読んでみてください。(※1)
では、そのプログラムを少し書き換えてみましょう。
Test1.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
global_var = "Global Varibale"
def get_global():
local_var = global_var # ①
return local_var
print(global_var)
print(get_global())
実行結果:
C:\TEMP\Document\@ITエンジニアライフ2>python Test1.py
Global Varibale
Global Varibale
Test2.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
global_var = "Global Varibale"
def get_global():
global_var = "Reset Local" # ②
local_var = global_var # ①
return local_var
print(global_var)
print(get_global())
C:\TEMP\Document\@ITエンジニアライフ2>python Test2.py
Global Varibale
Reset Local
Test3.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
global_var = "Global Varibale"
def get_global():
local_var = global_var # ①
global_var = "Reset Local" # ②
return local_var
print(global_var)
print(get_global())
C:\TEMP\Document\@ITエンジニアライフ2>python Test3.py
Global Varibale
「ちょっと待ってよ。違うでしょ」(※2)
Test2.py と、Test3.py の違いは、①と②の順番だけです。
Test2.pyの場合は、global_varというローカル変数を定義して、その値を
local_varいうローカル変数にセットしているので、global_varという
グローバル変数は、書き換えていません。
Test3.pyは、local_varいうローカル変数にglobal_varというグローバル変数を
セットした後、global_varというグローバル変数を書き換えようとして、
エラーになっています。
特に、ややこしいのは、関数の引数の初期化問題です。(※3)
Test4.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def foo(key,mydict={}):
mydict[key] = "value"
return mydict
def bar(key,mydict={}):
mydict[key] = "value"
return mydict
print(foo("A"))
print(foo("B"))
print(foo("C"))
print(bar("D"))
print(bar("E"))
print(bar("F"))
C:\TEMP\Document\@ITエンジニアライフ2>python Test4.py
{'A': 'value'}
{'A': 'value', 'B': 'value'}
{'A': 'value', 'B': 'value', 'C': 'value'}
{'D': 'value'}
{'D': 'value', 'E': 'value'}
{'D': 'value', 'E': 'value', 'F': 'value'}
グローバルでもなく、ローカルでもなく、関数グローバルとでも言えましょうか。(※4)
先ほどの様に、エラーになってくれればまだ、まだましです。
気づかずに、後になって、何か変だと感じた頃には、手遅れだった、という
ことも多々あるため、気を付ける必要があります。
Test5.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
def foo(key,mydict={}):
mydict[key] = "value"
return mydict
print(foo("A"))
print(foo("B",{}))
print(foo("C"))
foo("A") で、foo関数のmydictが初期化されます。
foo("B",{})は、単に空の辞書を引数として、処理されています。
foo("C")は、関数定義時に初期化されたmydictが再利用されています。
これは、判りにくいの極みではないでしょうか?
======= <<注釈>>=======
※1 Pythonの変数スコープの話
まずは、以下のお話を読んでみてください。
https://qiita.com/msssgur/items/12992fc816e6adf32cff
Pythonは変数のスコープが曖昧な事を忘れない
※2 「ちょっと待ってよ。違うでしょ」
Traceback (most recent call last):
File "Test3.py", line 10, in <module>
print(get_global())
File "Test3.py", line 6, in get_global
local_var = global_var
UnboundLocalError: local variable 'global_var' referenced before assignment
※3 関数の引数の初期化問題
https://docs.python.org/ja/3.6/faq/programming.html#why-are-default-values-shared-between-objects
なぜオブジェクト間でデフォルト値が共有されるのですか?
※4 関数グローバル
そんな言葉はありません。
デフォルト値は、関数が定義されたときに一度だけ生成されます。それは、関数ごとに
異なっているのですが、関数の中だけで、生き続けています。ローカル変数ならば、
都度、使い捨てになるはずなので、特にマルチタスク等では注意が必要でしょう。