今、話題の人工知能(AI)などで人気のPython。初心者に優しいとか言われていますが、全然優しくない! という事を、つらつら、愚痴っていきます

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 関数グローバル
 そんな言葉はありません。
 デフォルト値は、関数が定義されたときに一度だけ生成されます。それは、関数ごとに
異なっているのですが、関数の中だけで、生き続けています。ローカル変数ならば、
都度、使い捨てになるはずなので、特にマルチタスク等では注意が必要でしょう。

Comment(0)

コメント

コメントを投稿する