6
from tkinter import *

F=Tk()

i=1
while i<10:
    newButton = Button(F,text="Show Number",command=lambda:showNumber(i))
    newButton.pack(side=TOP)
    i+=1

def showNumber(nb):
    print(nb)

F.mainloop()

すべてのボタンが 10 を返します。
ボタン 1 リターン 1、ボタン 2 リターン 2 が欲しい...
助けてくれてありがとう

4

2 に答える 2

5

これは Python FAQ で説明されています:異なる値を持つループで定義されたラムダがすべて同じ結果を返すのはなぜですか? .


FAQの回答を引用:

これは、x がラムダに対してローカルではなく、外側のスコープで定義されており、ラムダが呼び出されたときにアクセスされるためです。定義されたときではありません…</p>

これを回避するには、値をラムダのローカル変数に保存して、グローバルの値に依存しないようにする必要があります…</p>

言い換えれば、新しい関数は の値を格納しているのではなくi、変数iを格納しています。そして、それらはすべてループの最後に値を持つ同じvariableを格納しています。実際、 の直前に を追加すると、すべてのボタンが数値ではなく文字列を出力することがわかります。i10i = 'spam'F.mainloop()spam

これは、クロージャ (定義環境に影響を与える可能性のある関数) を作成しようとしている場合に非常に便利です*

これを回避する最も簡単な方法は、デフォルト値を持つパラメーターを使用することです。デフォルト値は変数を保持しません。関数が定義されたときに評価される値だけです。そう:

newButton = Button(F,text="Show Number", command=lambda num=i: showNumber(num))

* この場合、実際には関与するクロージャーがないことに注意してください。これiは、外側のスコープではローカルではなくグローバルであるためです。しかし実際には、これは Python がグローバルに対して特別な処理を行っており、ここでクロージャーを必要としないためです。概念的には、あると考えれば、__closure__または__code__属性を見始めない限り、問題は発生しません。

于 2013-10-30T21:15:47.783 に答える