Python の global と nonlocal 文
◆ Python だと代入時に毎回ローカルスコープに変数を作成する
◆ 親スコープの変数に代入するには global か nonlocal の指定が必要
◆ 親スコープでも nonlocal で global に代入できない
◆ builtins を使えば 本当のグローバルスコープにできる
◆ 親スコープの変数に代入するには global か nonlocal の指定が必要
◆ 親スコープでも nonlocal で global に代入できない
◆ builtins を使えば 本当のグローバルスコープにできる
Python のドキュメントの文一覧を見ていて global と nonlocal なんてあったんだと思ったので使ってみました
なので 代入は全部ローカルスコープに変数を作る扱いになります
親スコープやグローバルスコープに代入するなら nonlocal や global が必要です
代入せず参照だけであれば ローカルスコープに変数がなければ親スコープを探してくれます
PHP も var などがない部分は似ていますが 関数内の関数は use を使って内側で使える変数を指定しないといけないです
参照するだけでも use を使う必要があって 代入もする場合は use で変数の参照として & 付きで指定する必要があります
そう考えると Python のほうが使いやすいと思います
関数の外側の変数を書き換えることはほとんどないので 今のところ global や nonlocal が必要になったことはないくらいですし
let のある JavaScript と比べるとこうなります
fni 関数内で value に代入してから value を表示します
JavaScript の方では let などがないので親スコープの value に代入します
Python の方ではローカルスコープに変数を作って代入します
fni の外側での value を表示すると結果が違います
nonlocal がないと
エラーになりました
nonlocal を使うと
カウントできていますね
ただ Python の場合はあまりこういうクロージャを積極的に使わないイメージがあります
クラスやジェネレータを使う方が多そうです
クラスの場合
ジェネレータの場合
fno 関数の value はそのままで global の value が変わってます
global は未定義でも使えますが nonlocal は親のどこかに変数が定義されてる必要があります
親スコープがグローバルスコープでも 親である以上行けそうですがダメでした
グローバルスコープは明確に使いわけないとダメみたいですね
nonlocal はあくまで関数内関数で親スコープを見るものです
モジュール単位のグローバルです
[module1.py]
[main.py]
どのモジュールでも最初から __builtins__ という変数に存在します
builtins モジュールを import して得られるものと一緒です
builtins に入っているものは 「__builtins__.」 を省略してグローバル変数として書けます
print とか dict とか str とか map とかそういうのが入ってます
ここには自分で追加することもできます
[module1.py]
[main.py]
こうすれば一応全体を対象にしたグローバル変数になります
できるというだけでやらないほうがいいと思います
普通にできる言語でも グローバル変数はあまり使うべきじゃないと思いますし
全体で値を共有したいなら 共有する変数を管理するモジュールを作ってそのモジュールのグローバル変数として扱う方がいいと思います
そのグローバル変数に依存するモジュールかどうかは import してるかどうかで判断できますし
[module1.py]
[main.py]
foo は 「__builtins__["name"] = "value"」 形式で bar は 「__builtins__.name = "value"」 形式です
モジュールの方は bar はエラーで foo のみ追加されていますが メインだと逆で foo はエラーで bar は追加されています
宣言とスコープ
Python の代入文って宣言する var や let みたいのがないので 親スコープの変数に代入するのか 今のスコープに変数を作るか判断できませんなので 代入は全部ローカルスコープに変数を作る扱いになります
親スコープやグローバルスコープに代入するなら nonlocal や global が必要です
代入せず参照だけであれば ローカルスコープに変数がなければ親スコープを探してくれます
PHP も var などがない部分は似ていますが 関数内の関数は use を使って内側で使える変数を指定しないといけないです
参照するだけでも use を使う必要があって 代入もする場合は use で変数の参照として & 付きで指定する必要があります
そう考えると Python のほうが使いやすいと思います
関数の外側の変数を書き換えることはほとんどないので 今のところ global や nonlocal が必要になったことはないくらいですし
let のある JavaScript と比べるとこうなります
const fno = () => {
let value = 10
const fni = () => {
value = 20
}
fni()
console.log(value)
}
fno()
// 20
def fno():
value = 10
def fni():
value = 20
fni()
print(value)
fno()
# 10
fni 関数内で value に代入してから value を表示します
JavaScript の方では let などがないので親スコープの value に代入します
Python の方ではローカルスコープに変数を作って代入します
fni の外側での value を表示すると結果が違います
使うとき
使う例を考えると クロージャの例として有名なカウンターでしょうかnonlocal がないと
def counter():
count = 0
def up():
count += 1
return count
return up
c = counter()
print(c())
print(c())
# UnboundLocalError: local variable 'count' referenced before assignment
エラーになりました
nonlocal を使うと
def counter():
count = 0
def up():
nonlocal count
count += 1
return count
return up
c = counter()
print(c())
print(c())
# 1
# 2
カウントできていますね
ただ Python の場合はあまりこういうクロージャを積極的に使わないイメージがあります
クラスやジェネレータを使う方が多そうです
クラスの場合
class Counter:
def __init__(self):
self.count = 0
def up(self):
self.count += 1
return self.count
c = Counter()
print(c.up())
print(c.up())
# 1
# 2
ジェネレータの場合
def counter():
count = 0
while True:
count += 1
yield count
c = counter()
print(c.__next__())
print(c.__next__())
# 1
# 2
global と nonlocal
nonlocal はスコープに変数を作らず親のスコープの変数を使うものでしたが global は名前通り global の変数を使いますvalue = 1
def fno():
value = 10
def fni():
global value
value = 20
fni()
print(value)
fno()
print(value)
# 10
# 20
fno 関数の value はそのままで global の value が変わってます
global は未定義でも使えますが nonlocal は親のどこかに変数が定義されてる必要があります
def f():
global value
value = 10
f()
print(value)
# 10
def f():
nonlocal value
value = 10
f()
print(value)
# SyntaxError: no binding for nonlocal 'value' found
親スコープがグローバルスコープでも 親である以上行けそうですがダメでした
value = 1
def f():
nonlocal value
value = 10
f()
print(value)
# SyntaxError: no binding for nonlocal 'value' found
グローバルスコープは明確に使いわけないとダメみたいですね
nonlocal はあくまで関数内関数で親スコープを見るものです
global と言っても
global と言っても Python の場合は本当の意味でのグローバルスコープはないですモジュール単位のグローバルです
[module1.py]
global_value = 1
[main.py]
import module1
print(dir(module1))
print(module1.global_value)
print(global_value)
>py main.py
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'global_value']
1
NameError: name 'global_value' is not defined
builtins
一応本当のグローバルスコープ代わりのもので builtins というのはありますどのモジュールでも最初から __builtins__ という変数に存在します
builtins モジュールを import して得られるものと一緒です
print(__builtins__)
# <module 'builtins' (built-in)>
import builtins
print(builtins is __builtins__)
# True
builtins に入っているものは 「__builtins__.」 を省略してグローバル変数として書けます
print とか dict とか str とか map とかそういうのが入ってます
ここには自分で追加することもできます
[module1.py]
__builtins__["global_value"] = 1
[main.py]
import module1
print(__builtins__.global_value)
print(global_value)
# 1
# 1
こうすれば一応全体を対象にしたグローバル変数になります
できるというだけでやらないほうがいいと思います
普通にできる言語でも グローバル変数はあまり使うべきじゃないと思いますし
全体で値を共有したいなら 共有する変数を管理するモジュールを作ってそのモジュールのグローバル変数として扱う方がいいと思います
そのグローバル変数に依存するモジュールかどうかは import してるかどうかで判断できますし
__builtins__ の登録方法の違い
上ではディクショナリとして [] を使って追加しましたが 追加方法はメインとその他モジュールで違うようです[module1.py]
try:
__builtins__["module_foo"] = 1
except:
print("error: module_foo")
try:
__builtins__.module_bar = 1
except:
print("error: module_bar")
[main.py]
default_keys = set(vars(__builtins__).keys())
import module1
try:
__builtins__["main_foo"] = 1
except:
print("error: main_foo")
try:
__builtins__.main_bar = 1
except:
print("error: main_bar")
for key in set(vars(__builtins__).keys()).difference(default_keys):
print("added key:", key)
>py main.py
error: module_bar
error: main_foo
added key: main_bar
added key: module_foo
foo は 「__builtins__["name"] = "value"」 形式で bar は 「__builtins__.name = "value"」 形式です
モジュールの方は bar はエラーで foo のみ追加されていますが メインだと逆で foo はエラーで bar は追加されています