最近、私はPythonをいじり始めましたが、クロージャーが機能する方法に変わったものを見つけました。次のコードを検討してください。
adders=[0,1,2,3]
for i in [0,1,2,3]:
adders[i]=lambda a: i+a
print adders[1](3)
これは、単一の入力を受け取り、その入力を数値で追加した関数を返す単純な配列を作成します。関数はfor
ループで構築され、イテレータi
はから0
まで実行され3
ます。これらの数値のそれぞれに対して、lambda
関数を作成i
して関数の入力に追加する関数が作成されます。最後の行は、パラメーターとして2番目のlambda
関数を呼び出し3
ます。驚いたことに、出力はでした6
。
期待していました4
。私の推論は:Pythonではすべてがオブジェクトであり、したがってすべての変数はオブジェクトへのポインターとして不可欠です。のlambda
クロージャーを作成するときにi
、が現在ポイントしている整数オブジェクトへのポインターを格納することを期待していましたi
。つまりi
、新しい整数オブジェクトが割り当てられた場合、以前に作成されたクロージャーには影響しません。悲しいことに、adders
デバッガー内で配列を検査すると、配列がそうであることがわかります。すべてlambda
の関数は最後の値を参照してくださいi
、3
で、その結果adders[1](3)
を返します6
。
次のことについて不思議に思います:
- クロージャは正確に何をキャプチャしますか?
lambda
現在の値をキャプチャして、値を変更してi
も影響を受けないように関数を説得する最もエレガントな方法は何i
ですか?
i
名前空間を残すにはどうすればよいですか?
print i
、ループの後でそれは機能しないと言っていました。しかし、私はそれを自分でテストし、今、私はあなたが何を意味するかを見ます-それは機能します。ループ変数がpythonのループ本体の後に残っているとは思いもしませんでした。
if
、with
、try
など