nonlocalの概要

Python 3で導入されたnonlocalキーワードは、ネストされた関数内から外側の関数に定義された変数を操作するための仕組みを提供します。通常、ネストされた関数はその関数自身のローカルスコープで変数を作成しますが、nonlocalを使うことで外側のスコープの変数にアクセスし、値を変更することができます。 これは、特にクロージャや状態を持つ関数(例えばカウンタ関数)を実装する際に非常に役立ちます。nonlocalなしでは、内部関数から外部関数の変数を変更することはできません。

nonlocalの使用例

基本的な例

以下は、nonlocalを使わない場合の例です。

def outer():
    x = 1
    def inner():
        x = 2
        print("inner:", x)
    inner()
    print("outer:", x)
outer()

結果は以下の通りです:

inner: 2
outer: 1

内部関数inner()x = 2としていますが、これはinner()内部のxを変更しただけであり、outer()xには影響を与えていません。 これに対して、nonlocalを使用すると以下のように動作が変わります。

def outer():
    x = 1
    def inner():
        nonlocal x
        x = 2
        print("inner:", x)
    inner()
    print("outer:", x)
outer()

結果:

inner: 2
outer: 2

このように、nonlocalを使うと、inner()内で外部関数outer()xを変更することが可能になります。

globalとの違い

nonlocalglobalキーワードと似た機能を持ちますが、その適用範囲が異なります。globalはモジュール全体で定義された変数を操作するのに対し、nonlocalは外部関数のスコープに限定されます。 次のコードでその違いを確認できます。

x = 0
def outer():
    x = 1
    def inner():
        global x
        x = 2
        print("inner:", x)
    inner()
    print("outer:", x)
outer()
print("global:", x)

結果は以下の通りです:

inner: 2
outer: 1
global: 2

この場合、globalを使うことでxの変更がモジュール全体に影響しています。

nonlocalの適用範囲

nonlocalはネストされた関数でのみ使用可能です。トップレベルの関数やグローバルスコープでは無効です。また、外側の関数で変数が定義されていない場合には、nonlocalを使用するとエラーが発生します。以下の例ではエラーが発生します。

def outer():
    def inner():
        nonlocal x  # エラー: xの外側スコープが見つからない
        x = 2
    inner()
outer()

nonlocalを使うためには、必ず外側のスコープに対象となる変数が存在している必要があります。

よくある使用例

nonlocalは、クロージャやカウンタのような状態を保持する関数を実装する際によく使用されます。以下はその一例です。

def make_counter():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return counter
counter = make_counter()
print(counter())  # 1
print(counter())  # 2
print(counter())  # 3

この例では、nonlocalを使ってカウンタの状態countを外側のスコープで保持しています。

まとめ

Pythonnonlocalキーワードは、ネストされた関数内で外側のスコープの変数を変更する便利な手段です。globalとは異なり、モジュールのグローバルスコープにアクセスするのではなく、外側の関数スコープにのみ影響を与えます。クロージャや状態を持つ関数を作成する際に非常に有用です。