5
def some_func(a): 
    def access_a():
        print(a)
    access_a()

の値を出力しますaaただし、ネストされた関数を次のように変更したい場合:

def some_func(a): 
    def change_a():
        a += 1
        print(a)
    change_a()

UnboundLocalError例外が発生します。

が非ローカル変数であることはわかっaていますが、宣言せずにアクセスできるのはなぜnonlocal aですか?

4

3 に答える 3

9

Python スコープ規則 101:

  1. 関数本体にバインドされた名前は、local明示的に宣言されていない限りglobal(Python 2.x および 3.x) またはnonlocal(Python 3.x のみ) と見なされます。これは、関数の本体で代入が発生する場合に当てはまります。バインドされる前にローカル変数を読み込もうとすると、もちろんエラーになります。

  2. 名前が読み取られても関数の本体にバインドされていない場合は、それを囲むスコープ (グローバル スコープがある場合は外側の関数) で検索されます。注意: 関数の引数は事実上のローカル名であるため、囲んでいるスコープで検索されることはありません。

a += 1は主に のショートカットでa = a + 1あるため、例aではローカルです (関数の本体にバインドされ、グローバルまたは非ローカルとして明示的に宣言されていません) が、バインドされる前にそれ ( の右側) を読み取ろうとすることに注意してくださいa = a+1

nonlocalPython 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変数が必要な場合は、おそらく適切なクラスが必要になる可能性がありますよりクリーンなソリューション(ただし、内部関数を返さず、内部でのみ使用する例ではそうではない可能性があります)。

于 2016-12-22T09:33:18.027 に答える
1

私はあなたのために2つの解決策があります:

#first one:
# try with list, compound data types dict/list
def some_func(a): 
    def change_a():
        a[0] += 1
        print(a[0])
    change_a()
some_func([1])
>>> 2


#second one
#reference pointer 
from ctypes import *
def some_func_ctypes(a):
    def change_a():
      a[0] += 1
      print a.contents, a[0]
    change_a()

i = c_int(1)
pi = pointer(i)
some_func_ctypes(pi)

>>> c_int(2) 2
于 2016-12-22T09:44:49.433 に答える
0

演算子を使用する+=と、新しい値が に割り当てられaます。これは、通訳の目にはローカルに変わります。

于 2016-12-22T08:37:55.207 に答える