web-dev-qa-db-ja.com

Pythonのローカル関数

次のPythonコードで、UnboundLocalErrorを取得します。私が理解しているように、ローカル関数はそれを含む関数のローカル変数を共有しますが、ここではそうではないようです。私はaがこのコンテキストでは不変の値であることを認識していますが、それは問題にはなりません。

def outer():
    a = 0
    def inner():
        a += 1
    inner()
outer()

UnboundLocalErrorの値が可変タイプにラップされている場合、a例外が発生しないため、内部関数は親関数のすべての参照のコピーを受け取ったように見えます。

誰かがここで動作を明確にして、適切なPythonのドキュメントをこれに示すことができますか?

34
Matt Joiner

これを「可変性」の問題と見なすことは正しいと私は思います。投稿したコードは「UnboundLocalError」をスローしますが、次のコードはスローしません。

def outer():
    a = 0
    def inner():
        print a
    inner()
outer()

Pythonでは、内部スコープの外部スコープから変数の値を再割り当てすることはできません(この場合は適用されないキーワード「global」を使用している場合を除く)。

このPython 2.6.2ドキュメントの「クラス」ドキュメントの下部セクションをチェックしてください:

9.2。Pythonスコープと名前空間

[…]名前がグローバルとして宣言されている場合、すべての参照と割り当ては、モジュールのグローバル名を含む中間スコープに直接移動します。それ以外の場合、最も内側のスコープの外側にあるすべての変数は読み取り専用です(そのような変数に書き込もうとすると、同じ名前の外側の変数は変更されないまま、最も内側のスコープに新しいローカル変数が作成されます)。

「UnboundLocalError」は、関数が実際に「a」という新しい変数を宣言し、すぐに「+ =」操作を実行しようとしているためですが、「a」にはまだ値がないため、これは失敗します。 (「a + = 1」を「a = a + 1」として表示すると、「a」が未定義の場合に問題を確認できます)。

一般に、「a」を変更する場合、人々が通常それを回避する方法は、可変リストを使用して「a」を渡すことです(リストや辞書など)。変更可能な型のコンテンツを介して「a」を変更できます(この設定でのテストでおそらく気づいたでしょう)。

お役に立てば幸いです。

31

変数を非ローカルとして指定して、クロージャの状態を保持する必要があるため、定義は次のようになります。

def outer():
a = 0
def inner():
    nonlocal a
    a += 1
inner()
11
vaduha

変数を引数としてバインドしてみてください。

def outer():
    a = 0
    def inner(a=a):
        a += 1

    inner()

outer()

適切なドキュメントを探して掘り下げます。

編集

内部関数は外部スコープに副作用を持たせたいので、リストのような変更可能なデータ型を使用する必要があります。整数と文字列は不変です。

def outer():
    a = [0]
    def inner():
        a[0] += 1
    inner()
    print a[0]
outer()
9
Unknown