87

次のコードは2回吐き出されますが、私はそれ1を見てからを期待しています。01

def pv(v) :
  print v

x = []
for v in range(2):
  x.append(lambda : pv(v))

for xx in x:
  xx()

裏で、Pythonラムダがローカル変数が指している参照にバインドすることを期待していました。しかし、そうではないようです。この問題は、ラムダが最新のC ++と同等のバインド(たとえば「boost :: bind」)を実行している大規模なシステムで発生しました。このような場合、スマートptrにバインドするか、ラムダのコピーをコピーして作成します。

では、ローカル変数をラムダ関数にバインドし、使用時に正しい参照を保持するにはどうすればよいですか?ガベージコレクターを備えた言語からこれを期待しないので、私はその振る舞いにかなり夢中になっています。

4

2 に答える 2

135

に変更x.append(lambda : pv(v))x.append(lambda v=v: pv(v))ます。

「Pythonラムダがバックグラウンドでローカル変数が指している参照にバインドする」ことを期待しますが、それはPythonの動作方法ではありません。Pythonは、関数が作成されたときではなく、関数が呼び出されたときに変数名を検索します。デフォルトの引数は、関数が呼び出されたときではなく、作成されたときに評価されるため、デフォルトの引数を使用すると機能します。

これはラムダについて特別なことではありません。検討:

x = "before foo defined"
def foo():
    print x
x = "after foo was defined"
foo()

プリント

after foo was defined
于 2012-05-04T16:36:46.487 に答える
37

ラムダのクロージャは、その値ではなく、使用されている変数への参照を保持するため、変数の値が後で変更されると、クロージャの値も変更されます。つまり、クロージャ変数の値は、関数が作成されたときではなく、関数が呼び出されたときに解決されます。(ここでのPythonの動作は、関数型プログラミングの世界では珍しいことではありません。その価値はあります。)

2つの解決策があります:

  1. デフォルトの引数を使用して、定義時に変数の現在の値をローカル名にバインドします。 lambda v=v: pv(v)

  2. ダブルラムダを使用して、すぐに最初のものを呼び出します。(lambda v: lambda: pv(v))(v)

于 2012-05-04T16:41:17.527 に答える