これは Python FAQ で説明されています:異なる値を持つループで定義されたラムダがすべて同じ結果を返すのはなぜですか? .
FAQの回答を引用:
これは、x がラムダに対してローカルではなく、外側のスコープで定義されており、ラムダが呼び出されたときにアクセスされるためです。定義されたときではありません…</p>
これを回避するには、値をラムダのローカル変数に保存して、グローバルの値に依存しないようにする必要があります…</p>
言い換えれば、新しい関数は の値を格納しているのではなくi
、変数i
を格納しています。そして、それらはすべてループの最後に値を持つ同じvariableを格納しています。実際、 の直前に を追加すると、すべてのボタンが数値ではなく文字列を出力することがわかります。i
10
i = 'spam'
F.mainloop()
spam
これは、クロージャ (定義環境に影響を与える可能性のある関数) を作成しようとしている場合に非常に便利です*。
これを回避する最も簡単な方法は、デフォルト値を持つパラメーターを使用することです。デフォルト値は変数を保持しません。関数が定義されたときに評価される値だけです。そう:
newButton = Button(F,text="Show Number", command=lambda num=i: showNumber(num))
* この場合、実際には関与するクロージャーがないことに注意してください。これi
は、外側のスコープではローカルではなくグローバルであるためです。しかし実際には、これは Python がグローバルに対して特別な処理を行っており、ここでクロージャーを必要としないためです。概念的には、あると考えれば、__closure__
または__code__
属性を見始めない限り、問題は発生しません。