初心者プログラマのひとり勉強部屋

若葉マークのプログラマー☺️

【python入門】pythonチュートリアルで詰まった点(nonlocal変数とglobal変数・スコープ)

こんにちは。
今回はpython3エンジニア基礎試験で詰まった点について記載します。
(nonlocal変数とglobal変数)




名前空間とスコープ
python3チュートリアルの「クラス」の分野です。

docs.python.org


チュートリアルに以下のような例文があります。

def scope_test():
    def do_local():
        spam = "local spam"

    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal 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 scope:", spam)


結果

After local assignment: test spam・・・疑問①
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam・・・疑問②
In global scope: global spam



疑問①
do_local()の後の結果が"local spam"ではなく"test spam"

疑問②
do_global()の後の結果が"global spam"ではなく"nonlocal spam"


答えは、スコープにあるようです。
◾️スコープ
プログラムの有効範囲を示すもの
pythonのスコープはインデントで決まる


つまり、今回のチュートリアルのスコープはこうなっているようです。

f:id:mocomo012:20210121223438p:plain
スコープ


疑問①
do_local()の後の結果が"local spam"ではなく"test spam"

検証結果
ローカル変数はlocalスコープ1内でのみ有効
試しにlocalスコープ1内でprintしてみた

def scope_test():
    def do_local():
        spam = "local spam"
        print("localスコープ1内のspam:",spam)  #追加

    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal 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 scope:", spam)


結果

localscope1内のspam: local spam  #ローカルスコープ内ならlocal 変数を参照
After local assignment: test spam  #別のローカルスコープになるため同じインデント(スコープ)の"test spam"を参照
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam




疑問②

do_global()の後の結果が"global spam"ではなく"nonlocal spam"

検証結果
nonlocal変数はインデントが同じ階層の別のローカルスコープ(local1,local2,local3,local)の中で、global変数より優先される
global変数はglobalスコープで適用される
試しにglobal変数を定義した関数を最初に呼び出し、そのあとにnonlocalを定義した関数を呼び出してみた

def scope_test():
        
    def do_local(): 
        spam = "local spam"
        
    def do_global():  #nonlocalより先に定義
        global spam
        spam = "global spam"

    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal spam"

    spam = "test spam"

    do_global()  #nonlocalより先に呼び出し
    print("After global assignment:", spam)
    do_nonlocal()
    print("After nonlocal assignment:", spam)
    do_local()  #local変数を最後に呼び出し
    print("After local assignment:", spam)
    
scope_test()
print("In global scope:", spam)


結果

After global assignment: test spam  #global変数を定義しているがlocalスコープの変数が優先された
After nonlocal assignment: nonlocal spam
After local assignment: nonlocal spam
In global scope: global spam  #globalスコープではglobal変数が適用される



(参考)

ちなみに、do_local()の中でdo_nonlocal()を定義すると、nonlocalの一つ外側のスコープであるdo_localのスコープ内ではnonlocalが優先され、2つ外側のスコープだとtest_spamになりました。
nonlocalは1つ外側のスコープまでなんですね。

def scope_test():
    def do_local():
        spam = "local spam"

    def do_nonlocal():
        spam = "local spam1"  #ローカル変数を定義
        def do_nonlocal1():  #内側にnonlocalを定義
            nonlocal spam
            spam = "nonlocal1 spam"
        do_nonlocal1()
        print ("nonlocalの1つ外側のスコープ",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 scope:", spam)


結果

After local assignment: test spam
nonlocalの1つ外側のスコープ nonlocal1 spam
After nonlocal assignment: test spam #nonlocalの2つ外側のスコープ
After global assignment: test spam
In global scope: global spam


スコープについてはこちらのサイト様が参考になりました。

blog.codecamp.jp

またこちらのサイト様は色々検証されていて参考になります🙏

snowtree-injune.com