【问题标题】:Tkinter Commands: Function arguments in lambdas?Tkinter 命令:lambdas 中的函数参数?
【发布时间】:2018-08-21 01:44:40
【问题描述】:

我的程序有一个 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)

每当我尝试运行它时,它总是在 cat 的最后一次迭代中通过,因为 tkinter 命令中有一些奇怪的性质。相反,我希望它与定义 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 出现问题?

【问题讨论】:

标签: python for-loop button tkinter lambda


【解决方案1】:

您需要在创建函数时将cat 中的数据绑定到该函数。有关说明,请参阅this question。有几种方法可以做到这一点,包括partialsclosures

这是您将部分应用到您的示例的方式:

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

相反,我们希望将迭代器的 绑定到每一步的函数。我们需要在这里复制迭代器的值。

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

【讨论】:

  • 不会check_update_cat随着循环中的每次迭代而改变,导致我之前遇到的同样的问题?
  • 不,我在答案中添加了一个解释和可运行的示例。希望这可以清除它
猜你喜欢
  • 1970-01-01
  • 2015-03-11
  • 2021-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多