Python の nonlocal と global



## はじめに

* `global` 文と `nonlocal` 文はいつ使うの？
    * 「関数の外側のスコープにある変数」を関数の内側のスコープで <font color="#5f9ea0">**変更**</font> したいとき

|予約語|変更対象となる変数|
|:-|:-|
|`global` 文|一番外側のスコープにある変数（グローバル変数）|
|`nonlocal` 文| `nonlocal` 文が使われた関数の外側のスコープにある変数|




## 1. global 文

* global 文はいつ使うの？
    * 関数の中でグローバル変数を変更したいとき


### O 正しいコード

```python
#
# 対話モード >>> に
# コピペで実行できます。
#

間隔 = 1

def 間隔を設定する(新しい間隔):
    global 間隔
    print(間隔)
    間隔 = 新しい間隔

間隔を設定する(2)  # 1
間隔  # 2
```


### X 誤ったコード



```python
間隔 = 1

def 間隔を設定する(新しい間隔):
    # 1. global 文を使わずに
    # global 間隔
    
    # 2. グローバル変数を参照してから
    print(間隔)
    
    # 3. ローカル変数を定義しようとすると
    間隔 = 新しい間隔  

# 4. 例外が発生します
間隔を設定する(2)
# UnboundLocalError:
#     cannot access local variable '間隔'
#     where it is not associated with a value

間隔  # 1
```

<details><summary>長い解説</summary>



```python
間隔 = 1

def 間隔を設定する(新しい間隔):
    # 1. global 文を使わずに
    # global 間隔
    
    # 2. 関数の外側の変数を参照してから
    print(間隔)
    
    # 3. 関数の外側の変数と同じ名前を変数を
    #    関数の内側で定義するようなコードを書くと
    間隔 = 新しい間隔  

# 4. 例外が発生します
間隔を設定する(2)  # print 関数は実行されない
# 結果
#     UnboundLocalError:
#         cannot access local variable '間隔'
#         where it is not associated with a value
#
# 直訳
#     未束縛ローカルエラー:
#         ローカル変数 '間隔' にアクセスできません、
#         ローカル変数 '間隔' が値に関連付けられていない状態で
#
# 意訳
#     代入される前のローカル変数を参照してしまったエラー:
#         ローカル変数 '間隔' に値が代入されていない状態で
#         ローカル変数 '間隔' にアクセスできません
#
# 解釈
#     エラー文を見る限り global を使わずに関数内で変数 '間隔' に代入を行うコードを書くと
#     関数内では変数 '間隔' はローカル変数として取り扱われるということでしょうか...？

間隔  # 1
```


* `UnboundLocalError`
    * どこで例外が発生するの？
        * 関数定義文 `def 間隔を設定する(新しい間隔):` ではなく
        * 関数呼び出し `間隔を設定する(2)` で例外が発生します
    * 関数は実行されないので `print(間隔)` は実行されません

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">【Pythonクイズ】<br>このコードを実行すると、エラーが発生します。何行目でどのような原因で、どのようなエラーが発生するでしょう？ <a href="https://t.co/WRnsy7iELY">pic.twitter.com/WRnsy7iELY</a></p>&mdash; けむにく＠競プロ (@kemuniku) <a href="https://twitter.com/kemuniku/status/1792462586921021928?ref_src=twsrc%5Etfw">May 20, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

</details>


<br />


## 2. nonlocal 文

* `nonlocal` っていつ使うの？
    * わざわざクラス定義文を使うのが面倒だなってとき

かなと個人的に思っています。うまく説明できないのですが...orz



### Step 1. nonlocal 文で実装

<!--

辞書で参照するの面倒だな... しかも 2.1 -> 2.2 は全然改善されてないむしろ長くなっている... もっと短く書きたい...

![l17403.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254825/ee601623-2522-d0f8-b811-ccebdf1c8234.png)

せや...

-->

```python
#
# 対話モード >>> に
# コピペで実行できます。
#
間隔 = 1  # クラス変数, 一番外側の変数

def 間隔を設定する(新しい間隔):
    global 間隔
    間隔 = 新しい間隔

def カウンターを作る():
    現在の値 = 0  # インスタンス変数, 外側の変数
    def カウントする():
        nonlocal 現在の値
        現在の値 = 現在の値 + 間隔
        return 現在の値
    return カウントする



カウントするA = カウンターを作る()
カウントするA()  # 1
カウントするA()  # 2
カウントするA()  # 3
間隔を設定する(2)
カウントするA()  # 5
カウントするA()  # 7
カウントするA()  # 9

カウントするB = カウンターを作る()
間隔を設定する(3)
カウントするB()  # 3
カウントするB()  # 6
カウントするB()  # 9
間隔を設定する(4)
カウントするB()  # 13
カウントするB()  # 17
カウントするB()  # 21

カウントするA()  # 13
カウントするA()  # 17
カウントするA()  # 21
```






### Step 2. nonlocal 文で実装

ちょっとクラスっぽく実装したいな...

<!--

あ〜なんか何回も `self` とか `cls` って書くの面倒だな...

![l17403.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254825/ee601623-2522-d0f8-b811-ccebdf1c8234.png)

そうだインスタンス変数を `nonlocal` でクラス変数 `global` で代替しよう。そうすれば `self` とか `cls` とか使わなくてよくなる...

-->

```python
#
# 対話モード >>> に
# コピペで実行できます。
#
間隔 = 1  # クラス変数, 一番外側の変数

def カウンターを作る():
    現在の値 = 0  # インスタンス変数, 外側の変数
    
    def 間隔を設定する(新しい間隔):
        global 間隔
        間隔 = 新しい間隔
    
    def カウントする():
        nonlocal 現在の値
        現在の値 += 間隔
        return 現在の値
    
    return {
        'カウントする': カウントする,
        '間隔を設定する': 間隔を設定する
    }

# インスタンスを作成して使用
カウンターA = カウンターを作る()
カウンターA['カウントする']()  # 1
カウンターA['カウントする']()  # 2
カウンターA['カウントする']()  # 3

カウンターA['間隔を設定する'](2)
カウンターA['カウントする']()  # 5
カウンターA['カウントする']()  # 7
カウンターA['カウントする']()  # 9

カウンターB = カウンターを作る()
カウンターB['間隔を設定する'](3)
カウンターB['カウントする']()  # 3
カウンターB['カウントする']()  # 6
カウンターB['カウントする']()  # 9

カウンターB['間隔を設定する'](4)
カウンターB['カウントする']()  # 13
カウンターB['カウントする']()  # 17
カウンターB['カウントする']()  # 21

カウンターA['カウントする']()  # 13
カウンターA['カウントする']()  # 17
カウンターA['カウントする']()  # 21
```


:::note info

なんやクソコードみせんなこの野郎😡って感じですが、この Step 2 の書き方は Step 3 と比較して `self` を使っていないことを心の片隅においておいてもよいかなと感じました... React の関数コンポーネントの書き方はこの Step 2 の書き方と似ています... 伝わるかはわからないのですがあとで React の関数コンポーネントのコードを示します...

:::


### Step 3. クラス定義文で実装

クラスで実装したいな...

```python
#
# 対話モード >>> に
# コピペで実行できます。
#
class カウンタークラス:
    間隔 = 1  # クラス変数

    def __init__(self):
        self.現在の値 = 0  # インスタンス変数
    
    @classmethod
    def 間隔を設定する(cls, 新しい間隔):
        cls.間隔 = 新しい間隔
    
    def カウントする(self):
        self.現在の値 += カウンタークラス.間隔
        return self.現在の値

# インスタンスを作成して使用
カウンターA = カウンタークラス()
カウンターA.カウントする()  # 1
カウンターA.カウントする()  # 2
カウンターA.カウントする()  # 3

カウンターA.間隔を設定する(2)
カウンターA.カウントする()  # 5
カウンターA.カウントする()  # 7
カウンターA.カウントする()  # 9

カウンターB = カウンタークラス()
カウンターB.間隔を設定する(3)
カウンターB.カウントする()  # 3
カウンターB.カウントする()  # 6
カウンターB.カウントする()  # 9

カウンターB.間隔を設定する(4)
カウンターB.カウントする()  # 13
カウンターB.カウントする()  # 17
カウンターB.カウントする()  # 21

カウンターA.カウントする()  # 13
カウンターA.カウントする()  # 17
カウンターA.カウントする()  # 21
```

















<br />


## 4. そのほかの言語 - self を書かない動向

### (1) Kotlin

Kotlin はクラス定義文の中で `this`, Python でいうところの `self` が省略できるらしいです。

<br />

### (2) Swift

Swift もまたクラス定義文の中で `self` が省略できるらしいです。そしてそれが主流らしいです。

> 本記事の結論はselfを書くべき、としていますが、現時点では逆の結論に至っています。それは下記の理由から:
> * closureにキャプチャされているselfを見つけやすくするため。
> * GitHubなどをみる限り、ポピュラーなほうはselfを省略するスタイル。

* [Swift: メンバ変数アクセス時にselfは書くべき？](https://muukii.medium.com/swift-%E3%83%A1%E3%83%B3%E3%83%90%E5%A4%89%E6%95%B0%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E6%99%82%E3%81%ABself%E3%81%AF%E6%9B%B8%E3%81%8F%E3%81%B9%E3%81%8D-7a1b1069bf9d)

<br />


### (3) JavaScript

React でコンポーネントという HTML の塊みたいなものを定義するときにクラスではなく関数で定義するようになりました。理由は `this`, Python でいうところの `self` を書くのが面倒になったからなのかなと思っています。

React の関数で定義されたコンポーネントは初見で見ると「？」ってなるので焦るのですが `self` って書くのが面倒だったのかなくらいに抑えておけばよいのかなと思っています... 小並感

<details><summary>React におけるコンポーネントの定義</summary>

クラスコンポーネントよりも関数コンポーネントのほうが短くなっているよねっていうのがポイントです。

<details><summary>クラス ... Step 3 的な書き方</summary>

`this.setState` で `this.state`, Python でいうところのインスタンス変数を全部更新しているのがポイント。なんで一部のプロパティだけでなくすべてのプロパティを更新しているのかは知りません。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="VwOybxZ" data-pen-title="Untitled" data-user="nico25" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/nico25/pen/VwOybxZ">
  Untitled</a> by domodomodomo (<a href="https://codepen.io/nico25">@nico25</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

</details>


<details><summary>関数 ... Step 2 的な書き方</summary>

`const [現在の値, set現在の値] = useState(0);` は所謂 getter と setter です。setter が起動すると値の更新を通知するようになっている **はず** です。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="MWdrogP" data-pen-title="nonlocal - 関数コンポーネント" data-user="nico25" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/nico25/pen/MWdrogP">
  nonlocal - 関数コンポーネント</a> by domodomodomo (<a href="https://codepen.io/nico25">@nico25</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

<br />

↓ useState は本当になにやっているかわからない...

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">もうちょっと踏み込むと<br>React hooksは宇宙なんだよなー。<br><br>なかのコードをちゃんと読んでないので、<br>勉強不足なだけなんだと思うが、<br><br>例えば公式の⬇<br><br>const [index, setIndex] = useState(0);<br><br>これを見た時に変物と関数がセットになっているが<br>裏のコードが想像出来ん。</p>&mdash; ゆきうさぎ＠フリーのシステム屋 (@__snow_rabbit__) <a href="https://twitter.com/__snow_rabbit__/status/1801630146681782780?ref_src=twsrc%5Etfw">June 14, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<br />

↓「クラスを使わない」ことが必ずしも「オブジェクト指向ではない」ことを表しているわけではないよね、という話をされているのだと思います。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">そう！たまたまクラスが<br><br>ライフサイクルによってスライスされてしまうので、コンポーズ困難である<br><br>という致命的な弱点を抱えているだけで、オブジェクト指向（の一部の側面というのが正しい？）の考え方が全く入る余地がないとは思えない… <a href="https://t.co/T4PrH8Dn4M">https://t.co/T4PrH8Dn4M</a></p>&mdash; honey32 (@honey321998) <a href="https://twitter.com/honey321998/status/1801646400952012912?ref_src=twsrc%5Etfw">June 14, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

</details>

</details>





<br />


### ◯ まとめ

バックエンドはわかりませんがフロントエンド, UI については `self` を書かない流れなのかなと感じました。


<br />

<!--

```
def Person(name, age):
    _name = name
    _age = age
        
    def increment_age():
        _age =: _age + 1
        
    return lo()

lain = Person("岩倉玲音", 14)
lain.increment_age()
lain.get_age()  # 15
```

-->



## おわりに

関数の中で定義する関数の使いどころとしては、他にも「デコレータ」と「カリー化, 部分的適用」があります。

* [Python のクロージャと nonlocal ってなに？](https://python.ms/closure-and-nonlocal/)