我的程序将lambda作为循环内tkinter对象的命令。我希望lambda传递函数的参数,该函数将根据该参数进行一些更改。
for cat in self.t_m_scope:
print(cat)
self.t_m_scopeObj[cat] = [tk.Checkbutton(self, text = cat, command = lambda: self.t_m_checkUpdate(cat)), []]
if self.t_m_scopeVars[cat][0]: self.t_m_scopeObj[cat][0].toggle()
self.t_m_scopeObj[cat][0].grid(row = 5, column = colNum, padx = (10,10), pady = (2,5), sticky = tk.W)
每当我尝试运行此命令时,由于tkinter命令中的某些怪异性质,它总是在cat的最后一次迭代中传递。相反,我希望它使参数与定义lambda时的迭代完全匹配。我不希望仅因为在迭代后单击检查按钮就更改了参数。
这是lambda调用的函数。
def t_m_checkUpdate(self, cat):
self.t_m_scopeVars[cat][0] = not self.t_m_scopeVars[cat][0]
for subcat in range(len(self.t_m_scopeVars[cat][1])):
if self.t_m_scopeVars[cat][0] != self.t_m_scopeVars[cat][1][subcat]:
self.t_m_scopeVars[cat][1][subcat] = not self.t_m_scopeVars[cat][1][subcat]
self.t_m_scopeObj[cat][1][subcat].toggle()
从某种程度上讲,该程序是什么,我试图响应于单击主检查按钮而打开和关闭一堆检查按钮。我知道要切换哪个检查按钮(因为我有很多按钮)的唯一方法是使用lambda将参数传递给函数。
我确实知道我可以使用if / elif / else找到当前迭代的值,然后传递一个字符串而不是一个变量,但是我真的不想这样做,因为它只是不可伸缩的。
如果有帮助,我的复选按钮的文本和我要传递的参数是相同的。
有没有解决方法,所以我可以保留当前迭代而Tkinter不会变得一团糟?
答案 0 :(得分:0)
创建时,您需要将cat
中的数据绑定到该函数。有关说明,请参见this question。有几种方法可以做到这一点,包括partials和closures。
这是将部分应用于示例的方法:
from functools import partial
for cat in ...:
check_update_cat = partial(self.t_m_checkUpdate, cat)
self.t_m_scopeObj[cat] = [tk.Checkbutton(self, text=cat, command=check_update_cat), []]
在您的代码中,lambda引用在循环外部范围中声明的迭代器变量(cat
)。需要注意的一件事是,循环的范围“泄漏”了迭代器变量,即,您仍然可以在循环之后访问它。 lambda函数通过引用引用迭代器变量,因此它们访问迭代器的最新值。
可运行的示例:
a = []
for i in range(5):
a.append(lambda: i)
a[0]() # returns 4
a[1]() # also returns 4
del i # if we delete the variable leaked by the for loop
a[0]() # raises NameError, i is not defined because the lambdas refer to i by reference
相反,我们希望在每个步骤将迭代器的 value 绑定到函数。我们需要在此处复制迭代器的值。
import functools
a = []
for i in range(5):
a.append(functools.partial(lambda x: x, i)) # i is passed by value here
a[0]() # returns 0
a[1]() # returns 1
del i
a[0]() # still returns 0