globalとnonlocalの宣言に関係する疑問点を検証してみました

global宣言を行った変数は、どこで変数を定義すればよいのか? 2つの関数から同じ変数に対してglobal宣言を行ったときも、変数の変更が連動するのか? global宣言とnonlocal宣言は同じ関数の中で同時に行えるのか?などなど、疑問を検証してみました。

global宣言 とnonlocal宣言を簡単に説明すると「global宣言を関数の中で行うと、関数の内側からglobal変数を変更することができる。」「nonlocal宣言を関数の中で行うと、関数の内側から、外側の関数の変数を変更することができる。」ことです。

ただ、実際に使う前に、「こんな使い方をしたらどうなるの?」という疑問が、たくさん浮かんできましたので、検証しました。

0.チュートリアル学習のポイントシリーズ

この記事は「チュートリアル学習のポイントシリーズ」の記事です。一連の記事のリンクは、以下を参照してください。
Pythonチュートリアルの学習のポイントを整理

1.global宣言

global宣言を関数の中で行うと、関数の内側からglobal変数を変更することができます。それでは、global宣言について検証してみましょう。

(1) global宣言の基本的な使い方

まずは、基本的な使い方について検証します。コード01では、xxx1(),xxx2(),xxx3()が入れ子構造の関数になっています。そして、1番内側のxxx3()の中で変数xについて、8行目でglobal宣言を行っています。

変数xは、関数の外(2行目)だけではなく、すべての関数内で宣言(4, 6, 9行目)されていますので、xxx1(),xxx2()内のxはローカル変数です。したがって、2行目で宣言したglobal変数は、関数内のxには影響を与えません。しかし、xxx3()では変数xをglobal宣言しているので、global変数を変更することができます。出力01の8行目では、xxx1()実行前にはx = 0だった変数xが、x = 3に変更されています。

#コード01
x = 0
def xxx1():
    x = 1
    def xxx2():
        x = 2
        def xxx3():
            global x
            x = 3 #変更不能体の代入でも、global変数が連動して変更される。
            print('xxx3:', x)
        print('xxx2:', x)
        xxx3()
        print('xxx2:', x)
    print('xxx1:', x)
    xxx2()
    print('xxx1:', x)
    
print('xxx0:', x)
xxx1()
print('xxx0:', x)
#出力01
xxx0: 0
xxx1: 1
xxx2: 2
xxx3: 3
xxx2: 2
xxx1: 1
xxx0: 3  #変数xが0から3に変更されている

少しだけ、難しくします。コード02はコード01の4行目のx = 1を削除したものです。その場合、xxx2()ではコード01と同様にglobal変数の影響を受けませんが、関数xxx1()では変数xが定義されていないので、global変数の影響を受けるようになります。したがって出力02の3行目、7行目が出力01とは違う出力結果になります。

#コード02
x = 0
def xxx1():
    #x = 1
    def xxx2():
        x = 2
        def xxx3():
            global x
            x = 3 
            print('xxx3:', x)
        print('xxx2:', x)
        xxx3()
        print('xxx2:', x)
    print('xxx1:', x)
    xxx2()
    print('xxx1:', x)
    
print('xxx0:', x)
xxx1()
print('xxx0:', x)
#出力02
xxx0: 0
xxx1: 0   #コード01のときは1だった
xxx2: 2
xxx3: 3
xxx2: 2
xxx1: 3   #コード01のときは1だった
xxx0: 3

(2) 関数の外側にグローバル宣言を行った変数が定義されていない場合

コード01では、2行目で「x = 0」と変数xを定義していましたが、コード03では削除しました。つまり、コード03は、本来global変数を定義すべき位置でxの値を定義していません。しかし、エラーにはならず、コード01と同様の出力ができました。

このように、global変数を関数の外側で定義しなくても、global宣言により 関数の内側から定義することが可能です。

#コード03
#x = 0
def xxx1():
    x = 1
    def xxx2():
        x = 2
        def xxx3():
            global x
            x = 3
            print('xxx3:', x)
        print('xxx2:', x)
        xxx3()
        print('xxx2:', x)
    print('xxx1:', x)
    xxx2()
    print('xxx1:', x)

xxx1()
print('xxx0:', x)

def yyy1():
    x = 11
    def yyy2():
        global x
        print('yyy2:',x)
    yyy2()

yyy1()   #xxx3()からの変数xの変更が、yyy2()にも反映される
#出力03
xxx1: 1
xxx2: 2
xxx3: 3
xxx2: 2
xxx1: 1
xxx0: 3
yyy1: 3

(3) 2つの別の関数内で同じ変数のglobal宣言をおこなった場合

コード03をもう一度見てみましょう。コード03では、xxx3()だけではなく、yyy2()の中でも変数xをglobal宣言しています。この場合、関数xxx3()内での変数xの変更が、yyy2()のxにも影響をあたえることがわかります。

2つの別の関数内で同じ変数xのglobal宣言をおこなった場合、global宣言を行った関数同士では、変数xが連動する共通の変数になります。

(4) 内側からだけではなく、外側からも変更できるのか

コード04は、コード03のあとに、「x = 4」「yyy1()」を追加しました。関数yyy1()の外側(29行目)での変更が、global宣言を行った関数yyy2()のxに影響を与えていることがわかります。

念のため検証を行いましたが、global変数ですので、関数の外側での変更がglobal宣言を行ったxxx3(), yyy2()内に影響を与えるのは当たり前の結果ではあります。

#コード04
def xxx1():
    x = 1
    def xxx2():
        x = 2
        def xxx3():
            global x
            x = 3
            print('xxx3:', x)
        print('xxx2:', x)
        xxx3()
        print('xxx2:', x)
    print('xxx1:', x)
    xxx2()
    print('xxx1:', x)

xxx1()
print('xxx0:', x)

def yyy1():
    x = 11
    def yyy2():
        global x
        print('yyy2:',x)
    yyy2()

yyy1()   #xxx3()からの変数xの変更が、yyy2()にも反映される

x = 4
yyy1()   #関数の外での変更も関数内に影響する
#出力04
xxx1: 1
xxx2: 2
xxx3: 3
xxx2: 2
xxx1: 1
xxx0: 3
yyy2: 3
yyy2: 4

(5) global宣言を行った変数はどこで定義すればよいのか

global宣言をおこなった変数は、どこで定義すればよいのでしょうか。ここでいう定義とは、実際に変数に値を代入し、変数の型などを決定することです。

コード05は、18行目のxxx1()の実行前にxの値を出力しようとしましたが、xが定義されていないというエラーが発生しました。xxx1()やyyy1()の外側のglobal変数xは、xxx1()を実行することによって、8行目で、はじめて変数が定義されます。変数が定義されなければ、変数の値がきまりませんので、出力することができずにエラーになります。「変数は実際にその変数を実行するまでに、変数の定義を行う」というのが原則です。

#コード05 →エラー
def xxx1():
    x = 1
    def xxx2():
        x = 2
        def xxx3():
            global x
            x = 3
            print('xxx3:', x)
        print('xxx2:', x)
        xxx3()
        print('xxx2:', x)
    print('xxx1:', x)
    xxx2()
    print('xxx1:', x)
    
print('xxx0:', x) #変数xが未定義
xxx1()
print('xxx0:', x)
#出力05
NameError: name 'x' is not defined

コード06も同様に8行目で変数xが定義される前にxを出力しようとしたのでエラーになりました。

#コード06
def xxx1():
    x = 1
    def xxx2():
        x = 2
        def xxx3():
            global x
            print('xxx3:', x) #変数xが未定義
            x = 3
            print('xxx3:', x)
        print('xxx2:', x)
        xxx3()
        print('xxx2:', x)
    print('xxx1:', x)
    xxx2()
    print('xxx1:', x)
    
xxx1()
print('xxx0:', x)

(6) 関数内で変数を定義した後にglobal宣言することはできるのか

コード07は、7行目で x = 3と定義した直後(8行目)にglobal宣言を行ったのですが、「すでにxは宣言されている」というエラーになってしまいました。8行目でglobal宣言をするまえに7行目でx = 3としているので、global宣言する前にローカル変数として定義されてしまいました。変数xを定義する前(変数xに値を代入する前)にglobal宣言を行う必要があります。

#コード07 →エラー
def xxx1():
    x = 1
    def xxx2():
        x = 2
        def xxx3():
            x = 3
            global x #globalの宣言をする前にローカル変数として定義されている
            print('xxx3:', x)
        print('xxx2:', x)
        xxx3()
        print('xxx2:', x)
    print('xxx1:', x)
    xxx2()
    print('xxx1:', x)
    
xxx1()
print('xxx0:', x)
#出力07
SyntaxError: name 'x' is assigned to before global declaration

2.nonlocal宣言

nonlocal宣言を関数の中で行うと、関数の内側から、外側の関数の変数を変更することができます。以下、nonlocal宣言について検証します。

(1) 外側の関数が複数ある場合

それではnonlocalについても、まず、基本的な働きについて検証します。コード08では、xxx1(),xxx2(),xxx3()が入れ子構造の関数になっています。そして、1番内側のxxx3()の中で変数xについて、8行目でnonlocal宣言を行っています。

nonlocal宣言を関数の中で行うと、関数の内側から、外側の関数の変数を変更することができるようになりますが、xxx3()の外側には2つの関数xxx2(),xxx3()が存在します。このように外側に複数の関数がある場合には、内側から優先的に検索します。つまり、 nonlocal宣言を行ったxxx3()の変数xと、そのすぐ外側のxxx2()の変数xが連動する共通の変数になります。

#コード08
x = 0
def xxx1():
    x = 1
    def xxx2():
        x = 2
        def xxx3():
            nonlocal x
            x = 3
            print('xxx3:', x)
        print('xxx2:', x)
        xxx3()
        print('xxx2:', x)
    print('xxx1:', x)
    xxx2()
    print('xxx1:', x)
    
print('xxx0:', x)
xxx1()
print('xxx0:', x)
#出力08
xxx0: 0
xxx1: 1
xxx2: 2
xxx3: 3
xxx2: 3   #すぐ外側の関数xxx2()のxの値が変更された
xxx1: 1
xxx0: 0

(2) すぐ外側の関数にnonlocal宣言された変数がない場合

すぐ外側の関数にnonlocal宣言された変数がない場合には、更に外側の関数に変数がないか検索されます。

コード09はコード08の6行目のx = 2を削除しました。最も内側の関数xxx3()でnonlocal宣言された変数xを9行目でx =3に変更すると、すぐ、外側のxxx2()に変数xが定義されていないか検索しますが、xxx2()ではxが定義されていないので、さらに外側のxxx1()の変数xがxxx3()の変数xと連動する共通の変数になります。
なお、関数xxx2()ではxが定義されていないので、xxx2()ではその外側のxxx1()のローカル変数xの影響をうけることになります。
つまり、xxx3()のxを変更すると、nonlocal宣言によって共通の変数となったxxx1()のxが変更されます。すると、xxx1()のローカル変数xの影響をうけるxxx2()においても、xの値が変わることになります。

#コード09
x = 0
def xxx1():
    x = 1
    def xxx2():
        #x = 2
        def xxx3():
            nonlocal x
            x = 3
            print('xxx3:', x)
        print('xxx2:', x)
        xxx3()
        print('xxx2:', x)
    print('xxx1:', x)
    xxx2()
    print('xxx1:', x)
    
print('xxx0:', x)
xxx1()
print('xxx0:', x)
#出力09
xxx0: 0
xxx1: 1
xxx2: 1 #xを定義していないので、xxx1()のローカル変数の影響をうける。
xxx3: 3
xxx2: 3 #xxx3のxの変更でxxx1のxが変更。さらに、xxx1のxの変更でxxx2のxが変更
xxx1: 3 #nonlocal宣言によりXXX3のXの変更の影響を受ける。
xxx0: 0

(3) 外側の関数のどこにもnonlocal宣言された変数がない場合

外側の関数xxx1()、xxx2()のどちらにもnonlocal宣言された変数xがない場合はどうなるのでしょうか。結果はエラーになりました。入れ子構造の関数の完全に外側、つまり、global変数に同じ名前の変数があったとしても、その値は参照しません。

コード10では、nonlocal宣言を行ったxxx3()の外側の関数xxx1(), xxx2()では変数xが定義されていません。したがってエラーの結果となりました。2行目でglobal変数としてxが定義されていますが、nonlocal宣言では、global変数は参照できません。

#コード10 →エラー
x = 0
def xxx1():
    #x = 1
    def xxx2():
        #x = 2
        def xxx3():
            nonlocal x
            x = 3
            print('xxx3:', x)
        print('xxx2:', x)
        xxx3()
        print('xxx2:', x)
    print('xxx1:', x)
    xxx2()
    print('xxx1:', x)
    
print('xxx0:', x)
xxx1()
print('xxx0:', x)
#出力10
SyntaxError: no binding for nonlocal 'x' found

3.global宣言とnonlocal宣言を同時に行えるのか

global宣言とnonlocal宣言は同時に行うことができません。コード11ののように、7行目でnonlocal、8行目でglobal宣言を行うとエラーになりました。

#コード02 →エラー
def xxx1():
    x = 1
    def xxx2():
        x = 2
        def xxx3():
            nonlocal x
            global x    #nonlocalとglobalの宣言を重ねられない。
            x = 3
            print('xxx3:', x)
        print('xxx2:', x)
        xxx3()
        print('xxx2:', x)
    print('xxx1:', x)
    xxx2()
    print('xxx1:', x)
    
xxx1()
print('xxx0:', x)
#出力11
SyntaxError: name 'x' is nonlocal and global

4.参考記事

以上、global宣言とnonlocal宣言の使い方について検証した結果です。スコープや、の内容が分かっていなければ、理解しにくいと思いますので、以下の記事も参考にしてください。

関数のスコープは3層入れ子構造(ネスト)の具体例で理解しよう。
変数や関数の定義位置について整理してみました