4

このコードを実行すると、次の結果が得られます。

15
15

出力は次のようになるはずです

15
17

そうではありません。問題は、なぜですか?

def make_adder_and_setter(x):
    def setter(n):
        x = n

    return (lambda y: x + y, setter)

myadder, mysetter = make_adder_and_setter(5)
print myadder(10)
mysetter(7)
print myadder(10)
4

3 に答える 3

8

Python 2.x には、読み取り/書き込みで変数をキャプチャすることを許可しない構文制限があります。

その理由は、変数が関数で割り当てられた場合、2 つの可能性しかないからです。

  1. 変数はグローバルであり、そのように宣言されていますglobal x
  2. 変数は関数のローカルです

より具体的には、変数が囲んでいる関数スコープのローカルであることは除外されます

nonlocalこれは、Python 3.x で宣言が追加されて置き換えられました。コードを次のように変更することで、Python 3 で期待どおりに動作します。

def make_adder_and_setter(x):
    def setter(n):
        nonlocal x
        x = n

    return (lambda y: x + y, setter)

Python 2.x ランタイムは、バイトコード レベルでクローズド オーバー変数の読み書きを処理できますが、制限はコンパイラが受け入れる構文にあります。

このビデオの最後で、読み取り/書き込みのキャプチャ状態で加算器クロージャを作成する Python バイトコードを直接生成する Lisp コンパイラを見ることができます。コンパイラは、Python 2.x、Python 3.x、または PyPy のバイトコードを生成できます。

Python 2.xa でクローズド オーバーの可変状態が必要な場合は、リストを使用するのがコツです。

def make_adder_and_setter(x):
    x = [x]
    def setter(n):
        x[0] = n

    return (lambda y: x[0] + y, setter)
于 2013-08-16T13:11:57.913 に答える
2

内部def setter(n)関数は独自のローカル変数を定義しますx。これにより、パラメーターであった他のx変数が非表示になりますmake_adder_and_setter(スコープに穴が開きます)。したがって、setter関数には副作用はありません。内部ローカル変数の値を設定して終了するだけです。

以下のコードを試してみると、おそらく明確になるでしょう。x の代わりに z という名前を使用するだけで、まったく同じことを行います。

def make_adder_and_setter(x):
    def setter(n):
        z = n

    return (lambda y: x + y, setter)

myadder, mysetter = make_adder_and_setter(5)
print myadder(10)
mysetter(7)
print myadder(10)
于 2013-08-16T13:12:29.187 に答える