使用 lambda 函数右键单击 Tkinter 中的分配问题

Assignment problems in Tkinter Right Click with lambda function

我有一个非常复杂的 Tkinter GUI,因此通过使用 lambda 函数避免重复分配控件的工作是值得的。这里的代码摘录:

    def createRightMenu(self, treeName, commands: []):
       for cmd in commands:
          self.rightMouse[treeName].add_command(label= cmd['label'], command= lambda: self.execCommand(cmd['reqId']))
       self.tree[treeName].bind("<Button-3>", lambda event: 
       self.rightMouse_click(event.x_root, event.y_root, treeName))

    def execCommand(self, reqId):
       print("execCommand", reqId)

    self.tree = {}
    treeName = 'a'
    self.createTree(root, treeName)
    self.createRightMenu(treeName , [
        {'label': 'copy', 'reqId': 1},
        {'label': 'retrieve', 'reqId': 2},
        ])

什么有效? 在我的右键单击菜单中,我得到条目 'copy' 和 'retrieve' 并且调用了 execCommand。

什么不起作用? execCommand 始终获取最后一个列表元素,即 'retrieve',2 作为参数,即使我右键单击 'copy'。如果我手动添加两个 .add_command,一切都很好。仅使用 for 循环是行不通的。知道为什么吗?

范围问题:当 lambda 触发时,它会检查 cmd 变量的范围。在 for cmd in commands: 的每次迭代期间,cmd 变量都会设置为一个新值。在迭代结束时,cmd 变量仍然等于 for cmd in commands: 的最终迭代(即 - {'label': 'retrieve', 'reqId': 2})。

解决方案是在 lambda 的签名中指定一个 "new" 变量,因为 python 中的作用域是如何工作的(引用 "new" 因为它可以是同名变量)。具体来说,您可以将代码更改为:

for cmd in commands:
    self.rightMouse[treeName].add_command(label= cmd['label'],
                                          command= lambda reqid = cmd['reqId']: self.execCommand(reqid))

下面是一个脚本,通过示例演示我在这里谈论的内容:

lambdas = []
words = ["Hello","World"]
print("Creating Lambds")
for word in words:
    my_lambda = lambda: word
    print('>>> Result for "{}" lambda: "{}"'.format(word,my_lambda()))
    lambdas.append(my_lambda)

print("Post Loop Lambda Results")
for w,_lambda in zip(words,lambdas):
    print('>>> Result for "{}" lambda: "{}"'.format(w,_lambda()))

word = "Foobar"
print('Setting word variable to "{}"'.format(word))
print("New Results for lambdas:")
for w,_lambda in zip(words,lambdas):
    print('>>> Result for "{}" lambda: "{}"'.format(w,_lambda()))

print("----------------\nSolution:\n")
lambdas = []
words = ["Hello","World"]
print("Creating Lambds")
for word in words:
    my_lambda = lambda myword = word: myword
    print('>>> Result for "{}" lambda: "{}"'.format(word,my_lambda()))
    lambdas.append(my_lambda)

print("Post Loop Lambda Results")
for w,_lambda in zip(words,lambdas):
    print('>>> Result for "{}" lambda: "{}"'.format(w,_lambda()))

word = "Foobar"
print('Setting word variable to "{}"'.format(word))
print("New Results for lambdas:")
for w,_lambda in zip(words,lambdas):
    print('>>> Result for "{}" lambda: "{}"'.format(w,_lambda()))

这里是一些链接: