◆ Python だと代入時に毎回ローカルスコープに変数を作成する
◆ 親スコープの変数に代入するには global か nonlocal の指定が必要
◆ 親スコープでも nonlocal で global に代入できない
◆ builtins を使えば 本当のグローバルスコープにできる

Python のドキュメントの文一覧を見ていて global と nonlocal なんてあったんだと思ったので使ってみました

宣言とスコープ

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 は追加されています