python3x: what would Python print?, local environment versus global environment etc

講義を聞いていたらpop quiz (for nothing unfortunately...)が出てきた。globalとlocalを理解する上で役に立つと思いまとめてみることにする。問題はこちら:

#What would Python print?#1

```
def f():
    return 0

def g():
    print(f())

def h():
    def f():
        return 1
    g()

h()
```

##My Initial Understanding to it

1. globalフレーム内で`func f()`、`func g()`、`func h()`が定義される。
2. `func h()`を呼んだので`func h()`の中身に入る。`func h()`内のローカル領域で`func f()`が定義され、`func g()`を呼ぶ。
3. <a href="http://www.pythontutor.com/visualize.html#code=def+f(%29%3A%0A++++return+0%0A%0Adef+g(%29%3A%0A++++print(f(%29%29%0A%0Adef+h(%29%3A%0A++++def+f(%29%3A%0A++++++++return+1%0A++++g(%29%0A%0Ah(%29&mode=display&origin=opt-frontend.js&cumulative=false&heapPrimitives=false&textReferences=false&py=3&rawInputLstJSON=%5B%5D&curInstr=0" target="_blank">pythontutor.com</a>を参考にすると分かりやすいが通常指定された変数を探す場合は今自分のいる`local frame`から上の層（一番上の層がglobal frame）に行く。従って`g()`を呼んで`func h()`の中のローカルである`def f(): return 1`をボトムアップで一番新しく定義された`def f(): return 1`にたどり着き`1`を返す。

ただこれには大きい誤解があるのではないかと思い始めてきた。

##Defining Environments

その前に関数を呼ぶ（新しいenvironmentもしくはフレームを作る）ということはどういうことなのか講義スライドを参考に考えてみる。

![Screen Shot 2016-02-10 at 10.38.00 PM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/e20057a9-5891-526f-890d-d50856aefc9c.png)

一番最後の:

>_when a user-defined function value is called, the local frame that is created for that call is linked to the defining frame of the function_

が最大のヒントだと思う。どこで呼ばれたかというよりは__どこで定義されたか__のほうが重要なのではないかと。つまり

1. globalフレーム内で`func f()`、`func g()`、`func h()`が定義される。
2. `func h()`を呼んだので`func h()`の中身に入る。`func h()`内のローカル領域で`func f()`が定義され、`func g()`を呼ぶ。
3. __呼ばれた関数が作られた領域へと移動する__（in this case, func g()'s parent frame is global, in which it was defined）。`func g()`の中に入って行くと`print(f())`があるので（グローバル領域内で）`func f()`を探す。
4. グローバル領域にある`func f()`は`def f(): return 0`なので`0`と表示される。

逆に言えば`func g()`は`func h()`のローカルフレームで定義された`def f(): return 1`にアクセスすることが出来ないのではないかと考えている。理由は単純に`func g()`を呼んだ時点でグローバル領域にいるので別に`func h()`を呼んで中に入るか、もしくは`func g()`を`func h()`の中に`func g()`を入れるかしないとアクセス出来ない。

##I wonder as I wander

ひとつ目の疑問は上の仮説が正しいのならばもし仮に`func g()`を`func h()`内にいれたら違う結果になるのか？

```
def f():
	return 0

def h():
	def f():
		return 1
	def g():
		print(f())
	g()

h() # 1
```

恐らくこれは正しいみたい。

2つ目はこれは関数だけに適用することなのか。スライド上ではあえて（それとも深い意味は無いのか？）function valueと言っていたのでそうなのかもしれない？？

考えてみたら変数に関しても同じ考え方で良いと思う。一番簡単な考え方は数ある同じ名前を持った変数`x`が合ったとして最終的に`return`もしくは`print`の一番近くにあるものが（もっと適切な言い方をすれば最後に`x`として再定義されたもの）が`print`されると考えていいのかもしれない。

#What would Python print?#2

続いての問題はこちら:

```
def f():
    return 0

g = f

def f():
    return 1

print(g())
```

個人的にはこっちのほうが簡単だった。単純に`func f()`が定義されて`g`も`func f()`とリンクされてその後に`func f()`がもう一つの（下の）`func f()`として再アサインされる。よって`g`は上の`func f()`となる。

![Screen Shot 2016-02-11 at 12.05.59 AM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/512e9810-50e0-e230-4891-6c4a84da84c3.png)

まさにスライドに書いてあるとおりこれは`x=3: y=x: x=4: print(y)`と同じ。ちなみにこれは上で書いた疑問の回答にもなる。

正確な動きを見たい場合は<a href="http://www.pythontutor.com/visualize.html#code=def+f(%29%3A%0A++++return+0%0A%0Ag+%3D+f%0A%0Adef+f(%29%3A%0A++++return+1%0A%0Aprint(g(%29%29&mode=display&origin=opt-frontend.js&cumulative=false&heapPrimitives=false&textReferences=false&py=3&rawInputLstJSON=%5B%5D&curInstr=0" target="_blank">pythontutor.com</a>を。

#What would Python print?#3

```
def f():
    return 0

def g():
    print(f())

def f():
    return 1

g()
```

これは上の問題を関数に変えたのと同じ。なので答えは`1`となる。

#What would Python print?#4

```
def g(x):
    print(x)

def f(f):
    f(1)

f(g)
```

実際に<a href="http://www.pythontutor.com/visualize.html#code=def+g(x%29%3A%0A++++print(x%29%0A%0Adef+f(f%29%3A%0A++++f(1%29%0A%0Af(g%29&mode=display&origin=opt-frontend.js&cumulative=false&heapPrimitives=false&textReferences=false&py=3&rawInputLstJSON=%5B%5D&curInstr=0">pythontutor.com</a>で見れば動きはわかるが、簡単流れは:

1. `g(x)`と`f(f)`が定義される。
2. `f(g)`で呼ぶ。この時parameter`f`を`func g(x)`として呼んでいるので実際は`f(g()) w/ no argument yet`となる。
3. 次に`f(1)`を呼んでいるがローカル領域上で`f`は`g()`にアサインされているので実際は`g(1)`を呼んでいるのと同じ。
4. `g(1)`を呼び`print(1)`つまりvalue`None`を返してinterpreter上に`1`と表示される。

![Screen Shot 2016-02-11 at 5.14.48 AM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/a97aa736-8396-9fa1-18a2-d80a83fb47bc.png)

#What would Python print?#5

```
def f():
    return 0

def g():
    return f()

def h(k):
    def f():
        return 1
    p = k
    return p()

print(h(g))
```

これは上の問題を統合して作ったというイメージ。上の問題ができればこれもすんなり出来るはず。ポイントは関数が呼ばれたら必ずしもローカルフレームから探すわけではないということ__その関数が定義されたところから探す__。ここでは`g()`はグローバルで定義されているので`h(k)`は見向きもせず上の`f()`を見付けて`0`を返す。

![Screen Shot 2016-02-11 at 5.33.25 AM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/1959c345-7aef-8e72-9446-306d38a0ad3f.png)

>_Assignments don't themselves create new values, but only copy old ones, so that when p is evaluated, it is equal to k, which is equal to g, which is attached to the global environment_

#What would Python print?#6

```
def f(p,k):
    def g():
        print(k)
    if k == 0:
        f(g,1)
    else:
        p()
f(None, 0)
```

これも`p()`（=`g()`）を呼んだ時に`g()`が定義されたところ（=global）に戻ってから`k`を探す。つまり答えは`0`。

![Screen Shot 2016-02-11 at 5.53.12 AM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/5e3df10b-bfa3-9e15-df5a-9332083c4b45.png)