循环中的 Python Lambda
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19837486/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Python Lambda in a loop
提问by FunkySayu
Considering the following code snippet :
考虑以下代码片段:
# directorys == {'login': <object at ...>, 'home': <object at ...>}
for d in directorys:
self.command["cd " + d] = (lambda : self.root.change_directory(d))
I expect to create a dictionary of two function as following :
我希望创建一个包含两个函数的字典,如下所示:
# Expected :
self.command == {
"cd login": lambda: self.root.change_directory("login"),
"cd home": lambda: self.root.change_directory("home")
}
but it looks like the two lambda function generated are exactly the same :
但看起来生成的两个 lambda 函数完全相同:
# Result :
self.command == {
"cd login": lambda: self.root.change_directory("login"),
"cd home": lambda: self.root.change_directory("login") # <- Why login ?
}
I really don't understand why. Do you have any suggestions ?
我真的不明白为什么。你有什么建议吗 ?
采纳答案by Roger Pate
You need to bind d for each function created. One way to do that is to pass it as a parameter with a default value:
您需要为每个创建的函数绑定 d。一种方法是将其作为具有默认值的参数传递:
lambda d=d: self.root.change_directory(d)
Now the d inside the function uses the parameter, even though it has the same name, and the default value for that is evaluated when the function is created. To help you see this:
现在函数内部的 d 使用参数,即使它具有相同的名称,并且在创建函数时评估它的默认值。为了帮助您看到这一点:
lambda bound_d=d: self.root.change_directory(bound_d)
Remember how default values work, such as for mutable objects like lists and dicts, because you are binding an object.
记住默认值是如何工作的,例如对于可变对象(如列表和字典),因为您正在绑定一个对象。
This idiom of parameters with default values is common enough, but may fail if you introspect function parameters and determine what to do based on their presence. You can avoid the parameter with another closure:
这种带有默认值的参数的习惯用法已经足够常见,但是如果您内省函数参数并根据它们的存在确定要做什么,则可能会失败。您可以使用另一个闭包来避免该参数:
(lambda d=d: lambda: self.root.change_directory(d))()
# or
(lambda d: lambda: self.root.change_directory(d))(d)
回答by robbie_c
This is due to the point at which d is being bound. The lambda functions all point at the variabled
rather than the current valueof it, so when you update d
in the next iteration, this update is seen across all your functions.
这是由于 d 被绑定的点。lambda 函数都指向变量d
而不是它的当前值,因此当您d
在下一次迭代中更新时,所有函数都会看到此更新。
For a simpler example:
举个更简单的例子:
funcs = []
for x in [1,2,3]:
funcs.append(lambda: x)
for f in funcs:
print f()
# output:
3
3
3
You can get around this by adding an additional function, like so:
您可以通过添加附加功能来解决此问题,如下所示:
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
You can also fix the scoping inside the lambda expression
您还可以修复 lambda 表达式内的范围
lambda bound_x=x: bound_x
However in general this is notgood practice as you have changed the signature of your function.
但是,通常这不是一个好习惯,因为您已经更改了函数的签名。
回答by MonsterBat Doppelg?nger
I met the same problem. The selected solution helped me a lot, but I consider necessary to add a precision to make functional the code of the question: define the lambda function outside of the loop. By the way, default value is not necessary.
我遇到了同样的问题。选定的解决方案对我有很大帮助,但我认为有必要增加一个精度以使问题的代码起作用:在循环外定义 lambda 函数。顺便说一下,默认值不是必需的。
foo = lambda d: lambda : self.root.change_directory(d)
for d in directorys:
self.command["cd " + d] = (foo(d))
回答by Georgy
Alternatively, instead of lambda
, you can use functools.partial
which, in my opinion, has a cleaner syntax.
或者,lambda
您可以使用functools.partial
它来代替,在我看来,它具有更清晰的语法。
Instead of:
代替:
for d in directorys:
self.command["cd " + d] = (lambda d=d: self.root.change_directory(d))
it will be:
这将是:
for d in directorys:
self.command["cd " + d] = partial(self.root.change_directory, d)
Or, here is another simple example:
或者,这是另一个简单的例子:
numbers = [1, 2, 3]
lambdas = [lambda: print(number)
for number in numbers]
lambdas_with_binding = [lambda number=number: print(number)
for number in numbers]
partials = [partial(print, number)
for number in numbers]
for function in lambdas:
function()
# 3 3 3
for function in lambdas_with_binding:
function()
# 1 2 3
for function in partials:
function()
# 1 2 3