Python : 名前空間とスコープ、クロージャ

参考書籍 : Python実践入門



名前空間の作成されるタイミング
組み込みオブジェクトの名前空間 - Python起動時
モジュールのグローバルな名前空間 - モジュール読み込み時
関数のローカルな名前空間 - 関数呼び出し時
クラスの名前空間 - クラス定義時



・スコープ
scratch.py

# scratchモジュールのグローバルスコープ

def f():
    # 関数f()のローカススコープ
    # 関数g()のエンクロージングスコープでもある
    pass

    def g():
        # 関数g()のローカルスコープ
        pass


def h():
    # 関数hのローカルスコープ
    pass

1, ローカルスコープ
関数(メソッド)の定義時や内包表記で作成されるスコープ
その関数内でしか参照できない

2, グローバルスコープ
モジュールのトップレベルのスコープ
同じモジュール内から参照できる。
別のモジュールで同じ名前の変数が定義されても、別のモジュールからはインポートしない限り参照できない
モジュールの中で、グローバル変数と同じ名前のローカル変数を作成し、ローカルスコープからその名前の変数にアクセスするとき、ローカル変数が優先される。
関数内(ローカル関数内)から、グローバル変数の値を買い替えたい場合は、globan文を使う。

3, エンクロージングスコープ
入れ子になった関数における、1つ外側の関数のローカルスコープ
ローカルスコープとグローバルスコープの中間になる。
入れ子になった関数からエンクロージングスコープの変数の値を書き換えるには、nonlocal文を使う。

4, ビルトインスコープ
組み込み関数やTrue, False, Noneなどの、組み込みオブジェクトの名前空間
一番最後に検索される。



クロージャ

def counter():
    count = 0

    def _increment():
        nonlocal count
        count += 1
        return count

    return _increment


counter1 = counter()
print(counter1)  # <function counter.<locals>._increment at 0x02FC7100>
print(counter1())  # 1
print(counter1())  # 2

クロージャである関数オブジェクト_increment()がエンクロージングスコープのcountを保持するため、_increment()が実行されるたびにcountがインクリメントされる。
別のカウンタを用意しても、違う変数への参照になりお互いに影響しない

counter2 = counter()
print(counter2())  # 1
print(counter2())  # 2