循环中的 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 14:45:32  来源:igfitidea点击:

Python Lambda in a loop

pythonloopsanonymous-function

提问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 variabledrather than the current valueof it, so when you update din 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.partialwhich, 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