打倒、Pythonのグローバル宣言

#global宣言
テーマはPython3のglabal宣言のスコープについて。
定義の部分が曖昧な理解だったため数時間無駄にしてしまったが、いろいろ調べて勉強になったので記録に残しておきます。

#失敗した例
すごく簡単に書くとこんなコード。あくまでサンプルです。

```Python:sample.py
def func():
    global x
    x = 20
    print(f'funcのx={x}')

def main():
    x = 10
    func()
    print(f'mainのx={x}')

if __name__ == '__main__':
    main()
``` 

どちらも20が出力されるかと思いきや。

```text:bash
$ python3 sample1.py 
funcのx=20
mainのx=10
```
なんで？？！！global宣言しとるやんけ！！とか思い数時間あれこれ調べたが解決。↓↓の通り。

###大事なのはスコープ
`global x`での宣言は下記2つを意味する。

- グローバルスコープにオブジェクトxがあれば、そのコードブロック内で**グローバル変数x**として扱える（＝同一identityを参照）
- グローバルスコープにオブジェクトxがなければ、新しく**グローバル変数x**として宣言（[グローバルスコープからもこのxは扱える](https://snowtree-injune.com/2018/12/13/global-nonlocal/#toc6)）

先ほどのコードだと実はxはグローバルスコープではなく、main()（＝ローカルスコープ）内に存在していた。つまり**func()内で宣言したグローバル変数xはmain()内のローカル変数xとは全く別物だった。**



###解決策
main()というローカルスコープ内ではなく、グローバルスコープ内でxを宣言しておきます。


```Python:sample.py
def func():
    global x
    x = 20
    print(f'funcのx={x}')

def main():
    # x = 10 ここはローカルスコープ
    func()
    print(f'mainのx={x}') #グローバル変数の参照はできる（詳細は本記事下部）

if __name__ == '__main__':
    x = 10 #グローバルスコープでxを定義
    main()
```
```text:bash
$ python3 test.py 
funcのx=20
mainのx=20
```

`def main` & `if __name__ == ‘__main__’:`なんてやり方をせずに、単純にmain()部分の命令を書き込んでおけば、このトラブルは起こらなかっただろう・・・。


#global宣言はそのコードブロック内だけで有効


```python:sample.py
def func():
    #global x ここではglobal宣言しない場合
    x = 20
    print(f'funcのx={x}')

def main():
    global x
    func()
    print(f'mainのx={x}')

if __name__ == '__main__':
    x = 10 #グローバルスコープでxを定義
    main()
```

```text:bash
$ python3 sample1.py
funcのx=20
mainのx=10
```
main()内でglobal宣言しているので、そこから呼び出されるfunc()内でglobal宣言は要らないかと思いきや、そんなことはなかった。

理由を考えるにあたり、ドグマである[公式ドキュメント](https://docs.python.org/ja/3/reference/simple_stmts.html#global)を参照。改めて読むと結構わかりやすいのね。
>global 文は、現在のコードブロック全体で維持される宣言文です。 global 文は、列挙した識別子をグローバル変数として解釈するよう指定することを意味します。 global を使わずにグローバル変数に代入を行うことは不可能ですが、自由変数を使えばその変数をグローバルであると宣言せずにグローバル変数を参照することができます。

**「現在のコードブロック全体」**で宣言は維持されるということは、逆に言うとコードブロック外には効果は及ばないということか。
func()内で`x = 20`が代入（定義）された瞬間、グローバル変数が書き換わるのではなく、ローカル変数xが定義されたのだと考えられる。

###解決策

```python:sample.py
def func():
    global x #func()内でもグローバル宣言
    x = 20
    print(f'funcのx={x}')

def main():
    global x
    func()
    print(f'mainのx={x}') 

if __name__ == '__main__':
    x = 10 #グローバルスコープでxを定義
    main()
```

```text:bash
$ python3 test.py 
funcのx=20
mainのx=20
```
main()内でもfunc()内でもglobal宣言しておけば、どちらも同一にグローバルスコープのxをグローバル変数xとして扱えるようになる。

#global宣言がなくても参照はできる。
参考まで。
global宣言しなくてもグローバルスコープのオブジェクトならば参照はできる。書き換えはもちろんできない。[値を代入した瞬間、ローカル変数として定義される](https://monozukuri-c.com/python-funclist-global-declaration/#toc6)。

```python:sample.py
def func():
    x = 20
    print(f'funcのx={x}')

def main():
    func()
    print(f'mainのx={x}') 

if __name__ == '__main__':
    x = 10 #グローバルスコープでxを定義
    main()
```

```text:bash
$ python3 test.py 
funcのx=20
mainのx=10
```

どちらもglobal宣言していない。global変数の参照だけならできるのでmain()はグローバルスコープのxを参照できる。func()は中で`x = 20`を実行した瞬間にローカル変数xが定義され、グローバルスコープのグローバル変数xとは全く別物として扱われるようになる。


#まとめ
慣れないことをやると色んなところで詰まるね。解決のためにドキュメント読んだりする中で学びが深まる（と信じたい）のでよしとしましょう。

以下、今回勉強する中で分かりやすかった記事など（本文中にもリンク張ってますが）

- [globalとnonlocalの宣言に関係する疑問点を検証してみました  |  Snow Tree in June](https://snowtree-injune.com/2018/12/13/global-nonlocal/#toc6)
- [<title>Python global宣言【書き換えの時に宣言が必要な理由】</title>](https://monozukuri-c.com/python-funclist-global-declaration/#toc2)
- [<title>nonlocal一題 - Qiita</title>](https://qiita.com/amedama/items/5970ae0f317796c5e723) ←こちらは本記事と直接関係ないですが、わかりやすかったのでご紹介。