python3: nonlocal vs global

復習を込めて授業をサラッと見ていたら`nonlocal`と`global`に。自問自答してパット答えられなかったのでqiitaに書くことに。

#global and nonlocal

そもそもなんでこんなのが必要なのかというと、(pythonでは)プログラムは一度処理を終えてしまうとメモリが自動的に消えてしまうので次回同じプログラムを開始させても初期状態からスタートしてしまう。状況によってはそれが不都合だとされることがある。そこで`nonlocal` (or `global`)。これを使えば処理を終えても指定した変数の記録を残して次の処理を実行できるというわけだ。詳しくは<a href="http://qiita.com/weedslayer/items/5980736c42e096c725a3#%E7%9B%B4%E6%84%9F%E7%9A%84%E3%81%AB%E3%82%8F%E3%81%8B%E3%82%8A%E3%82%84%E3%81%99%E3%81%9D%E3%81%86%E3%81%AA%E4%BE%8B">一番下の例</a>を。

>_`global` says that whenever it sees `x0`(that's when the `x0` is mentioned inside of `f2`), we will go for the definition of `x0` in the global frame.

> _`nonlocal` says whenever you see `x1` mentioned, we will look for `x1` in some frame outside of `f2`_

```
x = 0
def outer():
    x = 1
    def inner():
        x = 2
        print("inner:", x)
    inner()
    print("outer:", x)
outer()
print("global:", x)

# inner: 2
# outer: 1
# global: 0
```

これに`nonlocal`を加えると

```
x = 0
def outer():
    x = 1
    def inner():
        nonlocal x
        x = 2
        print("inner:", x)
    inner()
    print("outer:", x)
outer()
print("global:", x)

# inner: 2
# outer: 2
# global: 0
```
xの中身をそのままキープさせたい時などに役に立ちそう。

```
def stepper(num):
    def step():
        nonlocal num
        num += 1
        return num
    return step
>>> stepper(4)()
5
```

たまたま自分で作ったメモを見つけたのでこれを元に。。

```
def counter():
    count = 0
    def counter():
        count += 1
        return count
    return counter
# triggers unboundlocalerror since its not defined inside the counter func.

def counter2():
    count = 0
    def counter():
        count = 0
        count += 1
        return count
    return counter
"""
c = counter2() always returns 1
since each time the counter func gets called,
it creates another local frame with new info unless you have nonlocal within it
"""

def counter3():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return count
```

対して`global`は

```
x = 0
def outer():
    x = 1
    def inner():
        global x
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# inner: 2
# outer: 1
# global: 2
```

結果的に`global`でも`nonlocal`でもnested functionの中にある変数（1つのみ）を__globalとして扱うかnonlocal（ある意味local）として扱うか__だけの問題。`global`を使えば指定した変数はglobal変数を無視して`global x`が定義されたところにある`x`を参考にすると。

#ちょっとした問題

```
>>> x0, y0 = 0, 0
>>> def g1(x1, y1):
...     def f2():
...         nonlocal x1
...         global x0
...         x0, x1 = 1,2
...         y0, y1 = 1,2
...     f2()
...     print(x0, x1, y0, y1)
... 
>>> g1(0,0)
1, 2, 0, 0
>>print(x0, y0)
1, 0
```
`x0`と`x1`がグローバルかノンローカルの違いを表している。

この例はもっとわかりやすいかもしれない。`global`はnested funcitonの中に配置しないと意味が無い。

```
def f():
    global x
    def g(): x = 3 # Local x
    g()
    return x
>>> x = 0
>>> f()
0
```

```
def f():
    def g():
        global x
        x = 3
    g()
    return x
>>>x = 0
>>> f()
3 
```

#直感的にわかりやすそうな例

```
def make_switcher(f,g,x,y):
"""
a function, which takes in two funcs, f and g, and two #s, x and y. It returns a new function that performs f x-times, and then switches to g for y-times, and then back to f, etc. Assume that f and g each take one arg.
>>>crazy_ivan = make_switcher(lambda x: x*x, lambda x: x+1, 2, 1)
>>>crazy_ivan(4)
16
>>>crazy_ivan(5)
25
>>>crazy_ivan(4)
5
>>>crazy_ivan(4)
16
"""
    counter = 0
    def swithcer(n):
        nonlocal counter, f, g, x, y
	if counter == x:
	    counter = 0
	    f,g = g,f
            x, y = y, x
	counter += 1
	return f(n)
    return swithcer
```

#その他、参考になりそうなもの

pythontutor.comも役に立つが個人的には自分で書いていったほうが楽しいのでレクチャーにあったショートハンドを参考にしながら勉強するのも面白そうだ。分かりやすく、覚えやすい。

![Screen Shot 2016-03-22 at 5.45.33 PM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/78932e46-f0e7-f3b9-05d2-435ef4dde23a.png)