使用 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()))
这里是一些链接:
- Description of Python scoping on SO
- Python Docs for name resolution
- Class 定义中范围的描述
文档
我有一个非常复杂的 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()))
这里是一些链接:
- Description of Python scoping on SO
- Python Docs for name resolution
- Class 定义中范围的描述 文档