Python Lambda在一个循环中
考虑下面的代码片段:
# directorys == {'login': <object at ...>, 'home': <object at ...>} for d in directorys: self.command["cd " + d] = (lambda : self.root.change_directory(d))
我期望创build一个两个函数的字典如下:
# Expected : self.command == { "cd login": lambda: self.root.change_directory("login"), "cd home": lambda: self.root.change_directory("home") }
但它看起来像生成的两个lambda函数是完全一样的:
# Result : self.command == { "cd login": lambda: self.root.change_directory("login"), "cd home": lambda: self.root.change_directory("login") # <- Why login ? }
我真的不明白为什么。 你有什么build议吗 ?
您需要为每个创build的函数绑定d。 一种方法是将其作为parameter passing给默认值:
lambda d=d: self.root.change_directory(d)
现在,函数内部的d使用参数,尽pipe它具有相同的名称,并且在创build函数时评估其默认值。 为了帮助你看到这个:
lambda bound_d=d: self.root.change_directory(bound_d)
记住默认值是如何工作的,比如像列表和字典这样的可变对象,因为你绑定了一个对象。
这个带有默认值的参数的习惯用法已经够用了,但是如果你反思函数参数并根据它们的存在来决定该怎么做,那么可能会失败。 你可以用另一个闭包来避免参数:
(lambda d=d: lambda: self.root.change_directory(d))() # or (lambda d: lambda: self.root.change_directory(d))(d)
更好的是,重新devise你如何处理“命令”将有助于在这里,并应该在别处帮助。
这是由于d被绑定的点。 lambda函数都指向variables d
而不是指向它的当前值 ,因此当在下一次迭代中更新d
时,可以在所有函数中看到此更新。
举个简单的例子:
funcs = [] for x in [1,2,3]: funcs.append(lambda: x) for f in funcs: print f() # output: 3 3 3
你可以通过添加一个额外的函数来解决这个问题,比如:
def makeFunc(x): return lambda: x funcs = [] for x in [1,2,3]: funcs.append(makeFunc(x)) for f in funcs: print f() # output: 1 2 3
您也可以修复lambdaexpression式内的范围
lambda bound_x=x: bound_x
但是一般来说,这是不好的做法,因为你已经改变了你的函数的签名。
我遇到了同样的问题。 选定的解决scheme帮了我很多,但我认为有必要添加一个精度来使问题的代码function:在循环之外定义lambda函数。 顺便说一下,默认值是没有必要的。
foo = lambda d: lambda : self.root.change_directory(d) for d in directorys: self.command["cd " + d] = (foo(d))