10

重い計算の一種のキャッシュとして機能するラムダのリストが欲しかったのですが、次のことに気付きました。

>>> [j() for j in [lambda:i for i in range(10)]]
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]

それでも

>>> list([lambda:i for i in range(10)])
[<function <lambda> at 0xb6f9d1ec>, <function <lambda> at 0xb6f9d22c>, <function <lambda> at 0xb6f9d26c>, <function <lambda> at 0xb6f9d2ac>, <function <lambda> at 0xb6f9d2ec>, <function <lambda> at 0xb6f9d32c>, <function <lambda> at 0xb6f9d36c>, <function <lambda> at 0xb6f9d3ac>, <function <lambda> at 0xb6f9d3ec>, <function <lambda> at 0xb6f9d42c>]

つまり、ラムダは一意の関数ですが、どういうわけかすべて同じインデックス値を共有しています。

これはバグですか、それとも機能ですか? この問題を回避するにはどうすればよいですか? リスト内包表記に限定されません...

>>> funcs = []
... for i in range(10):
...     funcs.append(lambda:i)
... [j() for j in funcs]
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
4

5 に答える 5

11

ここに表示されているのは、クロージャーの効果です。ラムダは、後で使用するプログラムから状態をキャプチャしています。したがって、各ラムダは一意のオブジェクトですが、状態は必ずしも一意ではありません。

ここでの実際の「落とし穴」は、その時点でiの値ではなく、変数がキャプチャされることです。iこれは、はるかに簡単な例で説明できます。

>>> y = 3
>>> f = lambda: y
>>> f()
3
>>> y = 4
>>> f()
4

ラムダは変数への参照を保持し、ラムダの実行時にその変数を評価します。

これを回避するには、ラムダ内のローカル変数に割り当てます。

>>> f = lambda y=y:y
>>> f()
4
>>> y = 6
>>> f()
4

最後に、ループの場合、ループ変数は一度だけ「宣言」されます。したがって、ループ内のループ変数への参照は、次の反復を過ぎても持続します。これには、変数がリスト内包表記に含まれます。

于 2012-04-09T08:07:38.397 に答える
0

それがバグなのか機能なのかはわかりlambda:iませんが、ラムダ関数を形成する前に i を評価しないことが起こっています。したがって、これは文字通り、現在の i の値が何であれ評価する単なる関数です。これがどのように起こるかの別の例を次に示します。

>>> i=5
>>> x=lambda:i
>>> x()
5
>>> i=6
>>> x()
6

したがって、明らかに、起こっていることは同じことですが、例では 0 から 9 の範囲でこの順序で割り当てられているため、 i が 9 になります。

それを回避する良い方法は本当にないと思います。Python の Lambda 関数はかなり制限されています。本質的に関数型言語ではありません。

于 2012-04-09T08:06:53.937 に答える