
[code]

    def scope_test():
        def do_local():
            spam="local spam"
        def do_nonlocal():
            nonlocal spam
            spam="non-local spam"
        def do_global():
            global spam
            spam="global spam"
        spam="test spam"
        do_local()
        print("After local assignment:", spam)
        do_nonlocal()
        print("After nonlocal assignment:", spam)
        do_global()
        print("After global assignment:", spam)
    
    scope_test()
    print("In global assignment:", spam)
    
[/code]

出力はこうなる：

[code]

    After local assignment: test spam
    After nonlocal assignment: non-local spam
    After global assignment: non-local spam
    In global scope: global spam
    
[/code]

らしいのですが、"After global assignment: non-local spam"となる理由がわかりません。なぜ、do_global():
global spamの次にあるのに、"global spam"ではなく"non-local
spam"となるか、初心者でもわかるように、超簡単にご説明いただけないでしょうか。


CC BY-SA 3.0
2017-11-09T02:37:31.927
39439
2018-12-01T05:18:06.553
2018-04-01T21:36:14.003
25936
26114
question
python python3
関数内部でglobal宣言よりもnonlocal宣言が優先されるのはなぜですか？


そのプログラムでは合計３つの`spam`変数が登場しますが、区別できるようにコメントをつけるとこうなります。

[code]

    def scope_test():
        def do_local():
            spam="local spam" # spam1
        def do_nonlocal():
            nonlocal spam # spam2
            spam="non-local spam"
        def do_global():
            global spam # spam3
            spam="global spam"
        spam="test spam" # spam2
        do_local()
        print("After local assignment:", spam) # spam2
        do_nonlocal()
        print("After nonlocal assignment:", spam) # spam2
        do_global()
        print("After global assignment:", spam) # spam2
    
    scope_test()
    print("In global assignment:", spam) # spam3
    
[/code]

`do_local`は当然ローカル変数`spam`を宣言して代入しているだけなので、`scope_test`で宣言された変数`spam`(spam2)には影響しません。

`do_non_local`は`nonlocal
spam`により、グローバルスコープを除く外側のスコープに存在する一番階層が近い変数`spam`(この場合spam2)を使いますよ、と宣言することになります。よって`do_non_local`を実行後はspam2の値は`"non-
local spam"`になります。

(グローバルスコープとは一番外側のスコープです)

最後に`do_global`は`global
spam`によりグローバルスコープの変数`spam`を使いますよ、と宣言します。コードにはグローバル変数`spam`の宣言がありませんが、`do_global`内で`spam="global
spam"`を実行した時点で、グローバル変数`spam`(spam3)が作られます。

結局、`do_global`はspam2に影響しないので、`"non-local spam"`が出力されるということになります。


CC BY-SA 3.0
2017-11-09T03:09:23.560
39440
2017-11-09T04:53:10.093
2017-11-09T04:53:10.093
20206
20206
39439
answer


超簡単かどうかはわからないですが、単純に言えば

[code]

        print("After global assignment:", spam)
    
[/code]

これがscope_test()関数のスコープ内で実行されているのでglobalではなくnonlocalなspamを出力します。ヒントとして、もしこの行のインデントを取り払えば、それはもはやscope_test()の(nonlocalな)スコープではなくglobalなスコープでの実行になるので"global
spam"と出力されるでしょう。

do_global()というのは確かにグローバルスコープのspamを代入操作によって更新しますが、だからといって後続の

[code]

        print("After global assignment:", spam)
    
[/code]

がグローバルスコープで実行されるわけではないということです。つまり値の更新と評価に関係がないわけです。実際この文はnonlocalなスコープで呼び出されていますね。


CC BY-SA 3.0
2017-11-11T08:53:12.133
39493
2017-11-11T08:53:12.133
25980
39439
answer


do_localは問題の核心ではないので省いて説明します。

[code]

    def scope_test():    
        spam = "local spam"
        def do_nonlocal():     
            nonlocal spam
            spam = "non_local spam"        
        def do_global():
            global spam
            spam = "global spam"
    
        do_nonlocal()
        print("After non_local assignment:", spam)
        do_global()
        print("After global assignment:", spam)
    scope_test()
    print("In global assignment:", spam)
    
[/code]

**結果1**

[code]

    After non_local assignment: non_local spam
    After global assignment: non_local spam
    In global assignment: global spam
    
[/code]

上のコードは、このコードと等価です。

[code]

    spam = "global spam"
    def scope_test():    
    
        def do_nonlocal():     
            nonlocal spam
            spam = "non_local spam"        
        def do_global():
            pass
        spam = "test spam"
        do_nonlocal()
        print("After non_local assignment:", spam)
        do_global()
        print("After global assignment:", spam)
    scope_test()
    print("In global assignment:", spam)
    
[/code]

**結果2**

[code]

    After non_local assignment: non_local spam
    After global assignment: non_local spam
    In global assignment: global spam
    
[/code]

global宣言は、グローバルスコープ=トップレベルで変数を定義したということを意味します。

nonlocal宣言すらパスすると、こうなります。

[code]

    spam = "global spam"
    def scope_test():    
        def do_nonlocal():     
            pass     
        def do_global():
            pass
        do_nonlocal()
        print("After non_local assignment:", spam)
        do_global()
        print("After global assignment:", spam)
    scope_test()
    print("In global assignment:", spam)
    
[/code]

**結果3**

[code]

    After non_local assignment: global spam
    After global assignment: global spam
    In global assignment: global spam
    
[/code]

nonlocal宣言を使うための条件  
**・ネストされた関数内で宣言することが必要。(ここではdo_nonlocal配下)  
・宣言前にスコープ外であらかじめ変数を定義しておくことが必要。(scope_test配下)  
・global宣言を受けた変数ではない。**

nonlocal宣言を書けば,その変数は、ネスト関数配下のローカル変数とは扱われなくなります。

[code]

    def do_global():
        global spam
        spam = "global spam"
    
[/code]

**一番の疑問**  
で、do_nonlocal()を呼び出した後で、do_global()を呼び出しているのに、なぜ、  
spamは、上書きされないのでしょうか？  
このspamは、結果2と同じ定め方をされているので、do_global()では、上書きできないのだと考えます。


CC BY-SA 3.0
2018-01-06T08:04:40.360
40771
2018-04-03T07:55:51.807
2018-04-03T07:55:51.807
24284
24284
39439
answer


簡単に言えば、関数内にグローバルと同名の変数がある場合のみ  
それぞれの変数は別の変数に変身します。  
つまりノンローカルが優先されているのではなく  
グローバルがその変数に関与していないだけです。  
ためしに

[code]

    do_local()
    print("After local assignment:", spam)
    do_nonlocal()
    print("After nonlocal assignment:", spam)
    
[/code]

を消して実行してみてください。意味がわかると思います。


CC BY-SA 4.0
2018-11-30T20:32:20.260
50796
2018-12-01T05:18:06.553
2018-12-01T05:18:06.553
3060
31230
39439
answer