Python スコープ規則 101:
関数本体にバインドされた名前は、local
明示的に宣言されていない限りglobal
(Python 2.x および 3.x) またはnonlocal
(Python 3.x のみ) と見なされます。これは、関数の本体で代入が発生する場合に当てはまります。バインドされる前にローカル変数を読み込もうとすると、もちろんエラーになります。
名前が読み取られても関数の本体にバインドされていない場合は、それを囲むスコープ (グローバル スコープがある場合は外側の関数) で検索されます。注意: 関数の引数は事実上のローカル名であるため、囲んでいるスコープで検索されることはありません。
a += 1
は主に のショートカットでa = a + 1
あるため、例a
ではローカルです (関数の本体にバインドされ、グローバルまたは非ローカルとして明示的に宣言されていません) が、バインドされる前にそれ ( の右側) を読み取ろうとすることに注意してくださいa = a+1
。
nonlocal
Python 3 では、次のステートメントでこれを解決できます。
>>> def outer(a):
... def change():
... nonlocal a
... a += 1
... print("before : {}".format(a))
... change()
... print ("after : {}".format(a))
...
>>> outer(42)
before : 42
after : 43
Python 2 にはそれがないnonlocal
ため、標準的なハックは変数を変更可能なコンテナーにラップすることです (通常は alist
ですが、任意の変更可能なオブジェクトで可能です)。
>>> def outer(a):
... _a = [a]
... def change():
... _a[0] += 1
... print("before : {}".format(_a[0]))
... change()
... print ("after : {}".format(_a[0]))
...
>>> outer(42)
before : 42
after : 43
これは控えめに言ってもかなり醜いです。
クロージャーは非常に便利ですが、ほとんどの場合、オブジェクトの機能的な対応物です。この状態のカプセル化を維持しながら、一連の関数間で状態を共有する方法です。したがって、nonlocal
変数が必要な場合は、おそらく適切なクラスが必要になる可能性がありますよりクリーンなソリューション(ただし、内部関数を返さず、内部でのみ使用する例ではそうではない可能性があります)。