【名前空間とスコープ】知っているとエラーが減る?|初心者にもわかりやすく解説【Python入門・応用18】

Python入門・応用18.名前空間スコープサムネイル
Python入門・応用講座

こんにちは、キノコードです。
この動画では、Pythonの名前空間とスコープについて説明します。

この記事の執筆・監修

キノコード
キノコード

テクノロジーアンドデザインカンパニー合同会社のCEO。
日本最大級のプログラミング教育のYouTubeチャンネル「キノコード」や、プログラミング学習サービス「キノクエスト」を運営。
著書「あなたの仕事が一瞬で片付くPythonによる自動化仕事術」や、雑誌「日経ソフトウエア」や「シェルスクリプトマガジン」への寄稿など実績多数。

名前空間

まず、名前空間について説明します。
名前空間とは、名前とオブジェクトを対応付けることです。
ここでいう名前とは、変数名や関数名などのことです。
対応付けるとは、その名前を使用して、モジュールやクラス、関数、要素などに直接アクセスできるようにすることです。
ほとんどの名前空間はキーに名前、値にオブジェクトを持つ辞書で対応付けられています。
また、組み込み関数のglobals関数を使うと、現在のモジュールの名前空間が返ってきます。
具体例で確認してみましょう。

a = 1

def func():
    b = 2

print(globals())

まず、変数aに1を代入します。
次に、関数funcを定義し、変数bに2を代入する処理を書きます。
そして、globals関数を使ってこのときの名前空間を表示してみましょう。
実行します。
名前空間が表示されました。
辞書の最後を見ると、aには1が対応付けられており、funcには関数が対応付けられていることがわかります。
その他にも、デフォルトで様々な名前空間があることが確認できます。

スコープ

次にスコープについて説明します。
スコープとは、名前空間に対応付けられている名前を直接参照できる領域のことです。
名前の有効領域とも言えます。
例えば、関数の中で定義された変数は、関数の外から直接参照することはできません。
確認してみましょう。

def func():
    b = 2

print(b)

先ほどと同様の関数funcを定義します。
そして、関数の外からbを表示してみましょう。
実行します。
このようにエラーになります。
他にも、レッスン16で説明した関数内関数も関数の外から直接参照できないということを説明しました。
このように、名前空間の中の名前には直接参照できる領域があり、その領域のことをスコープと言います。
Pythonのスコープには大きく分けて3種類あり、外側から順にビルトインスコープ、グローバルスコープ、ローカルスコープと呼びます。
Pythonでは、内側のスコープからは外側のスコープの名前を参照できますが、外側のスコープから内側のスコープの名前を参照できません。
この3種類のスコープについて、一つずつ確認していきましょう。

ビルトインスコープ

まず、ビルトインスコープについて説明します。
ビルトインスコープは一番外側のスコープです。
組み込み定数や組み込み関数など、定義したりインポートしなくても使用できる定数や関数が対応付けられています。
例えば、TrueやFalse、Noneなどの組み込み定数や、print関数やmax関数などの組み込み関数はプログラムのどの領域でも使えます。
全てのプログラムがビルトインスコープの中に入るので、コードを書くときにあまり意識する必要はありません。

グローバルスコープ

次に、グローバルスコープについて説明します。
グローバルスコープはビルトインスコープの内側のスコープで、1つのモジュール全体を含みます。
そのため、グローバルスコープはモジュールスコープとも言います。
グローバルスコープの名前には、そのモジュール内のどこからでも参照できます。
例えば、関数やクラスの外で定義された変数は、そのPythonファイル内のどこからでも参照できます。
具体例で確認してみましょう。

a = 1

def func():
    b = 2
    print(a)

print(a)
func()

まず、変数aに1を代入します。
次に、関数funcを定義します。
変数bに2を代入し、print関数でaを表示させる処理を書きます。
そして、関数funcの外側でaを表示してみましょう。
また、関数funcを呼び出してみましょう。
実行します。
エラーにならずに結果が表示されました。
変数aには関数の外からでも、関数の中からでも参照できることがわかります。

a = 1

def func():
    b = 2
    print(a)

print(globals())

また、globals関数を使うと、グローバルスコープの名前空間が返ってきます。
先ほどの変数と関数を定義した後に、globals関数を使ってこのときの名前空間を表示してみましょう。
実行します。
グローバルスコープの名前空間が表示されました。
辞書の最後の方を確認すると、変数aや関数funcはありますが、関数の中で定義された変数bは表示されていません。
この関数の中の領域が次に説明するローカルスコープです。

ローカルスコープ

次に、ローカルスコープについて説明します。
ローカルスコープはグローバルスコープの内側のスコープで、関数やクラスの中の領域です。
ローカルスコープの名前には、ローカルスコープの中からしか参照できません。
例えば、関数やクラスの中で定義された変数は、その関数やクラスの中からしか参照できません。
具体例で確認してみましょう。

def func():
    a = 1
    print(a)

func()

まず、関数funcを定義します。
変数aに1を代入し、print関数でaを表示させる処理を書きます。
そして、funcを呼び出してみましょう。
実行します。
1と表示されました。
このprint関数は変数aと同じローカルスコープ内で呼び出されているので、変数aを参照できます。

def func():
    a = 1

print(a)

今度はローカルスコープの外側からprint関数でaを表示してみましょう。
実行します。
エラーになりました。
このprint関数は変数aのローカルスコープの外側から呼び出されているので、変数aを参照できません。

a = 0

def func():
    a = 1
    print(a)

func()
print(a)

次は関数の前で、変数aに0を代入します。
また、関数の中でもaを表示させる処理を追加します。
そして、関数を呼び出し、その後にaを表示してみましょう。
実行します。
1と0が表示されました。
名前空間の名前を参照するときは、ローカルスコープ、グローバルスコープ、ビルトインスコープの順で参照します。
従って、関数funcの中でaを表示させるときは、ローカルスコープにあるaが優先的に表示されます。
一方、関数の外でaを表示させるときは、ローカルスコープにあるaは参照できないので、グローバルスコープにあるaが表示されます。
なお、ローカルスコープで定義された変数をローカル変数、グローバルスコープで定義された変数をグローバル変数と言います。

a = 0

def func():
    print(a)
    a = 1

func()

今度は、関数funcの中の処理の順番を変え、print関数でaを表示してから、aに1を代入します。
そして、関数funcを呼び出してみましょう。
実行します。
エラーになりました。
名前空間の名前を参照するときは、グローバルスコープよりもローカルスコープが優先されます。
従って、関数funcを定義する前にグローバルスコープでaが定義されていますが、ローカルスコープにあるprint関数はローカルスコープにあるaを参照しようとします。
しかし、ローカルスコープのaはprint関数よりも後で定義されているので、エラーになります。
このように、ローカル変数は変数を定義した後だけではなく、ローカルスコープ全体で参照されるので注意しましょう。

また、locals関数を使うと、locals関数を呼び出したスコープの名前空間が返ってきます。
確認してみましょう。

a = 0

def func():
    a = 1
    print(locals())

func()
print(locals())

先ほどと同様に変数と関数を定義し、関数の中でlocals関数を使ってこのスコープの名前空間を表示してみましょう。
また、関数の外でもlocals関数を使って名前空間を表示してみましょう。
実行します。
それぞれの名前空間が表示されました。
関数の中では、aには1が対応付けられていることがわかります。
そして、関数の外ではaには0が対応付けられており、funcには関数が対応付けられていることがわかります。
なお、グローバルスコープで呼び出す場合、locals関数とglobals関数の結果は同じになります。

また、関数内関数を定義すると、ローカルスコープの中に更にローカルスコープが作られます。
確認してみましょう。

a = 0

def func1():
    a = 1

    def func2():
        a = 2
        print(a)

    func2()
    print(a)

func1()
print(a)

最初に、変数aに0を代入し、関数func1を定義します。
関数func1の中で、変数aに1を代入し、関数func2を定義します。
関数func2の中で、変数aに2を代入し、aを表示させます。
そして、関数func1の中でfunc2を呼び出し、aを表示させます。
最後に、func1を呼び出し、aを表示させてみましょう。
実行します。
2、1、0と表示されました。
このプログラムは次のように処理されます。
まず、func1が呼び出されます。
次は、func1の中のfunc2が呼び出されます。
そして、func2の中のローカルスコープではaに2が対応付けられているので、2が表示されます。
次に、func1の中のaが表示されます。
func1の中のローカルスコープではaに1が対応付けられているので、1が表示されます。
最後に、グローバルスコープのaが表示されます。
グローバルスコープではaに0が対応付けられているので、0が表示されます。

global文

次に、global文について説明します。
今まで見てきたように、ローカルスコープで定義した変数はローカルスコープでしか参照できませんでした。
global文を使って変数を宣言すると、ローカルスコープで定義した変数をグローバル変数として定義できます。
具体例で確認してみましょう。

a = 0

def func1():
    a = 1

    def func2():
        global a
        a = 2
        print(a)

    func2()
    print(a)

func1()
print(a)

先ほど定義した関数内関数を使います。
func2の中で、global文を使って変数aを宣言します。
結果を確認してみましょう。
実行します。
2、1、2と表示されました。
func2の中でglobal文を使って変数aが宣言されたあと、aには2が代入されているので、グローバル変数のaには2が対応付けられます。
ただし、func1の中ではaには1が対応付けられています。
結果として、2、1、2と表示されます。

nonlocal文

最後に、nonlocal文について説明します。
nonlocal文を使って変数を宣言すると、宣言したスコープより一つ外側のスコープの変数として定義できます。
ただし、一つ外側のスコープがグローバルスコープの場合は使用できません。
具体例で確認してみましょう。

a = 0

def func1():
    a = 1

    def func2():
        nonlocal a
        a = 2
        print(a)

    func2()
    print(a)

func1()
print(a)

先ほどと同様の関数内関数を使います。
func2の中で、nonlocal文を使って変数aを宣言します。
結果を確認してみましょう。
実行します。
2、2、0と表示されました。
func2の中でnonlocal文を使って変数aが宣言されたあと、aには2が代入されているので、func2の一つ外側のスコープであるfunc1の中の変数のaには2が対応付けられます。
ただし、グローバル変数のaには0が対応付けられています。
結果として、2、2、0と表示されます。

a = 0

def func1():
    nonlocal a
    a = 1

    def func2():
        a = 2
        print(a)

    func2()
    print(a)

func1()
print(a)

今度はfunc1の中で、nonlocal文を使って変数aを宣言します。
結果を確認してみましょう。
実行します。
エラーになりました。
func1の一つ外側のスコープはグローバルスコープになるので、nonlocal文は使えません。
この場合は、global文を使って変数を宣言しましょう。

a = 0

def func1():
    global a
    a = 1

    def func2():
        a = 2
        print(a)

    func2()
    print(a)

func1()
print(a)

先ほどのnonlocal文をglobal文に書き換えました。
結果を確認してみましょう。
実行します。
2、1、1と表示されました。
グローバル変数のaには1が対応付けられていることがわかります。