如何创建Python lambdas列表(在列表理解/for循环中)?

时间:2022-10-30 10:51:24

I want to create a list of lambda objects from a list of constants in Python; for instance:

我想从Python中的常量列表中创建一个lambda对象列表;例如:

listOfNumbers = [1,2,3,4,5]
square = lambda x: x * x
listOfLambdas = [lambda: square(i) for i in listOfNumbers]

This will create a list of lambda objects, however, when I run them:

这将创建一个lambda对象列表,但是,当我运行它们时:

for f in listOfLambdas:
    print f(),

I would expect that it would print

我希望它能打印出来

1 4 9 16 25

Instead, it prints:

相反,它打印:

25 25 25 25 25

It seems as though the lambdas have all been given the wrong parameter. Have I done something wrong, and is there a way to fix it? I'm in Python 2.4 I think.

似乎所有的lambdas都被给出了错误的参数。我做错什么了吗?有没有办法补救?我想我在Python 2.4中。

EDIT: a bit more of trying things and such came up with this:

编辑:多一些尝试的事情,这就产生了:

listOfLambdas = []
for num in listOfNumbers:
    action = lambda: square(num)
    listOfLambdas.append(action)
    print action()

Prints the expected squares from 1 to 25, but then using the earlier print statement:

从1到25打印期望的正方形,然后使用前面的打印语句:

for f in listOfLambdas:
    print f(),

still gives me all 25s. How did the existing lambda objects change between those two print calls?

还是给了我25秒。在这两个打印调用之间,现有的lambda对象是如何变化的?

Related question: Why results of map() and list comprehension are different?

相关问题:为什么map()和list的理解结果不同?

8 个解决方案

#1


17  

I'm guessing that the lambda you're creating in the list comprehension is bound to the variable i which eventually ends up at 5. Thus, when you evaluate the lambdas after the fact, they're all bound to 5 and end up calculating 25. The same thing is happening with num in your second example. When you evaluate the lambda inside the loop it's num hasn't changed so you get the right value. After the loop, num is 5...

我猜你在列表理解中创建的lambda与变量I绑定最终结果是5。因此,当你在后面计算时,它们都是5,最后都是25。在第二个示例中,num也是如此。当你在循环中求值时它的num没有变化所以你得到了正确的值。循环之后,num为5…

I'm not quite sure what you're going for, so I'm not sure how to suggest a solution. How about this?

我不太确定你想要什么,所以我不确定如何提出一个解决方案。这个怎么样?

def square(x): return lambda : x*x
listOfLambdas = [square(i) for i in [1,2,3,4,5]]
for f in listOfLambdas: print f()

This gives me the expected output:

这就给出了期望输出:

1
4
9
16
25

Another way to think of this is that a lambda "captures" its lexical environment at the point where it is created. So, if you give it num it doesn't actually resolve that value until its invoked. This is both confusing and powerful.

另一种理解方式是lambda在创建时“捕获”其词汇环境。所以,如果你给它num,它实际上不会解析那个值,直到它被调用。这既令人困惑又强大。

#2


18  

You have:

你有:

listOfLambdas = [lambda: i*i for i in range(6)]

for f in listOfLambdas:
    print f()

Output:

输出:

25
25
25
25
25
25

You need currying! Aside from being delicious, use this default value "hack".

你需要局部套用!除了美味之外,使用这个默认值“hack”。

listOfLambdas = [lambda i=i: i*i for i in range(6)]

for f in listOfLambdas:
    print f()

Output:

输出:

0
1
4
9
16
25

Note the i=i. That's where the magic happens.

注意我=。这就是奇迹发生的地方。

#3


4  

When function statements are executed they are bound to their (lexically) enclosing scope.

当执行函数语句时,它们被绑定到它们的(词汇上)包围作用域。

In your snippet, the lambdas are bound to the global scope, because for suites are not executed as an independently scoped unit in Python. At the end of the for loop, the num is bound in the enclosing scope. Demo:

在您的代码片段中,lambdas被绑定到全局作用域,因为在Python中,套件不是作为独立作用域单元执行的。在for循环的末尾,num被绑定在封闭范围内。演示:

for num in range(1, 6):
    pass
assert num == 5 # num is now bound in the enclosing scope

So when you bind identifiers in the for loop you're actually manipulating the enclosing scope.

所以当你在for循环中绑定标识符时,你实际上是在操作封闭范围。

for num in range(1, 6):
    spam = 12
assert num == 5 # num is now bound in the enclosing scope
assert spam == 12 # spam is also bound in the enclosing scope

Same deal for list comprehensions:

同样的交易清单的理解:

[num for num in range(1, 6)]
assert num == 5

Mind blowing, I know. Anywho, with our newfound knowledge, we can determine that the lambdas you are creating are referring to the (single) num identifier bound in the enclosing scope. That should make this make more sense:

给人印象极深的,我知道。通过我们新发现的知识,我们可以确定您所创建的lambdas是指在封闭范围内绑定的(单个)num标识符。这应该更有意义:

functions = []
for number in range(1, 6):
    def fun():
        return number
    functions.append(fun)
assert all(fun() == 5 for fun in functions)
assert all(fun() is number for fun in functions)

And here's the coolest part that demonstrates it even more:

这是最酷的部分,更能说明这一点:

# Same as above -- commented out for emphasis.
#functions = []
#for number in range(1, 6):
#    def fun():
#        return number
#    functions.append(fun)
#assert all(fun() == 5 for fun in functions)
#assert all(fun() is number for fun in functions)
number = 6 # Rebind 6 in the scope and see how it affects the results.
assert all(fun() == 6 for fun in functions) 

So the solution to this, of course, is to make a new enclosing scope for each number you want to bind. In Python, you can create new enclosing scopes with modules, classes, and functions. It's common to use a function just to create new enclosing scope for another function.

因此,解决这个问题的方法,当然是,为你想要绑定的每个数字创建一个新的封闭范围。在Python中,您可以使用模块、类和函数创建新的封闭作用域。通常使用一个函数来为另一个函数创建新的封闭范围。

In Python, a closure is a function that returns another function. Kind of like a function constructor. Check out get_fun in the following example:

在Python中,闭包是返回另一个函数的函数。有点像函数构造函数。在下面的示例中查看get_fun:

def get_fun(value):
    """:return: A function that returns :param:`value`."""
    def fun(): # Bound to get_fun's scope
        return value
    return fun

functions = []
for number in range(1, 6):
    functions.append(get_fun(number))
assert [fun() for fun in functions] == range(1, 6)

Since get_fun is a function, it gets to have its own internal scope. Every time you call get_fun with a value, a little table is created to keep track of bindings within it; i.e. it says, "Within this scope, the value identifier points to the thing that was passed." That scope goes away at the end of the function execution, unless there's a reason for it to hang around.

因为get_fun是一个函数,所以它有自己的内部作用域。每次调用get_fun时,都会创建一个小表来跟踪其中的绑定;也就是说,“在这个范围内,值标识符指向传递的对象。”这个作用域在函数执行的末尾消失,除非有理由让它继续存在。

If you're returning a function from within a scope, that's a good reason for parts of the "scope table" to hang around -- that function you're returning could reference things from that scope table when you call it later on. For that reason, when fun is created within get_fun Python tells fun about get_fun's scope table, which fun keeps handy for when it's needed.

如果您要从范围内返回一个函数,这是“范围表”的部分要挂起的一个很好的理由——您要返回的函数可以在稍后调用该范围表时引用该范围表中的内容。出于这个原因,当在get_fun Python中创建fun时,它会告诉fun关于get_fun的范围表,当需要时,这个表就很方便。

You can read more about the details and technical terminology (which I softened a bit) in the Python docs on the execution model. You can also look at the parts of the enclosing scope that a function refers to with print fun.__closure__. In the above, we see the reference to the value, which happens to be an int:

您可以在执行模型上阅读更多关于Python文档中的细节和技术术语(我稍微软化了一点)。您还可以查看函数在print fun.__closure__中引用的封闭范围的部分。在上面,我们看到了对值的引用,它恰好是一个int:

# Same as before, commented out for emphasis.
#functions = []
#for number in range(1, 6):
#    functions.append(get_fun(number))
#assert [fun() for fun in functions] == range(1, 6)
print functions[0].__closure__
# Produces: (<cell at 0x8dc30: int object at 0x1004188>,)

#4


2  

listOfLambdas = [lambda i=i: square(i) for i in listOfNumbers]

Or

listOfLambdas = map(lambda i: lambda: square(i), listOfNumbers)

#5


1  

Try to use () instead of []:

试着用()代替[]:

listOfLambdas = (lambda: square(i) for i in listOfNumbers)

And you will get:

你将得到:

1
4
9
16
25

#6


0  

I sometimes find that defining actual classes for function objects makes it easier to understand what's going on:

我有时发现,为函数对象定义实际的类可以更容易地理解发生了什么:

>>> class square(object):
...   def __init__(self, val):
...     self.val = val
...   def __call__(self):
...     return self.val * self.val
...
>>> l = [1,2,3,4,5]
>>> funcs = [square(i) for i in l]
>>> for f in funcs:
...   print f()
...
1
4
9
16
25
>>>

Granted, it's a bit more verbose than using lambdas or closures, but I find this easier to understand when I'm trying to do non-obvious things with functions.

当然,它比使用lambda或闭包要冗长一些,但是当我尝试用函数做不明显的事情时,我发现这更容易理解。

#7


0  

You could also do:

你也可以做的事:

>>> def squares():
...     for i in [1,2,3,4,5]:
...         yield lambda:i*i
... 
>>> print [square() for square in squares()]
[1, 4, 9, 16, 25]

#8


0  

As an additional comment, I would like to outline the possibility to generate lists of lambda functions from sympy matrices (I don't know if it is the best way to do it, but this is how I do and I find it convenient):

作为补充说明,我想概述一下从症状矩阵生成lambda函数列表的可能性(我不知道这是否是最好的方法,但我就是这么做的,我觉得很方便):

import sympy as sp
sp.var('Ksi')
# generate sympy expressions for Berstein's polynomials
B_expr = sp.Matrix([sp.binomial(3, i) * Ksi**i * (1 - Ksi)**(3-i) for i in range(4)])
# lambdify them 
B = [sp.lambdify((Ksi), B_expr[i]) for i in range(4) ]

#1


17  

I'm guessing that the lambda you're creating in the list comprehension is bound to the variable i which eventually ends up at 5. Thus, when you evaluate the lambdas after the fact, they're all bound to 5 and end up calculating 25. The same thing is happening with num in your second example. When you evaluate the lambda inside the loop it's num hasn't changed so you get the right value. After the loop, num is 5...

我猜你在列表理解中创建的lambda与变量I绑定最终结果是5。因此,当你在后面计算时,它们都是5,最后都是25。在第二个示例中,num也是如此。当你在循环中求值时它的num没有变化所以你得到了正确的值。循环之后,num为5…

I'm not quite sure what you're going for, so I'm not sure how to suggest a solution. How about this?

我不太确定你想要什么,所以我不确定如何提出一个解决方案。这个怎么样?

def square(x): return lambda : x*x
listOfLambdas = [square(i) for i in [1,2,3,4,5]]
for f in listOfLambdas: print f()

This gives me the expected output:

这就给出了期望输出:

1
4
9
16
25

Another way to think of this is that a lambda "captures" its lexical environment at the point where it is created. So, if you give it num it doesn't actually resolve that value until its invoked. This is both confusing and powerful.

另一种理解方式是lambda在创建时“捕获”其词汇环境。所以,如果你给它num,它实际上不会解析那个值,直到它被调用。这既令人困惑又强大。

#2


18  

You have:

你有:

listOfLambdas = [lambda: i*i for i in range(6)]

for f in listOfLambdas:
    print f()

Output:

输出:

25
25
25
25
25
25

You need currying! Aside from being delicious, use this default value "hack".

你需要局部套用!除了美味之外,使用这个默认值“hack”。

listOfLambdas = [lambda i=i: i*i for i in range(6)]

for f in listOfLambdas:
    print f()

Output:

输出:

0
1
4
9
16
25

Note the i=i. That's where the magic happens.

注意我=。这就是奇迹发生的地方。

#3


4  

When function statements are executed they are bound to their (lexically) enclosing scope.

当执行函数语句时,它们被绑定到它们的(词汇上)包围作用域。

In your snippet, the lambdas are bound to the global scope, because for suites are not executed as an independently scoped unit in Python. At the end of the for loop, the num is bound in the enclosing scope. Demo:

在您的代码片段中,lambdas被绑定到全局作用域,因为在Python中,套件不是作为独立作用域单元执行的。在for循环的末尾,num被绑定在封闭范围内。演示:

for num in range(1, 6):
    pass
assert num == 5 # num is now bound in the enclosing scope

So when you bind identifiers in the for loop you're actually manipulating the enclosing scope.

所以当你在for循环中绑定标识符时,你实际上是在操作封闭范围。

for num in range(1, 6):
    spam = 12
assert num == 5 # num is now bound in the enclosing scope
assert spam == 12 # spam is also bound in the enclosing scope

Same deal for list comprehensions:

同样的交易清单的理解:

[num for num in range(1, 6)]
assert num == 5

Mind blowing, I know. Anywho, with our newfound knowledge, we can determine that the lambdas you are creating are referring to the (single) num identifier bound in the enclosing scope. That should make this make more sense:

给人印象极深的,我知道。通过我们新发现的知识,我们可以确定您所创建的lambdas是指在封闭范围内绑定的(单个)num标识符。这应该更有意义:

functions = []
for number in range(1, 6):
    def fun():
        return number
    functions.append(fun)
assert all(fun() == 5 for fun in functions)
assert all(fun() is number for fun in functions)

And here's the coolest part that demonstrates it even more:

这是最酷的部分,更能说明这一点:

# Same as above -- commented out for emphasis.
#functions = []
#for number in range(1, 6):
#    def fun():
#        return number
#    functions.append(fun)
#assert all(fun() == 5 for fun in functions)
#assert all(fun() is number for fun in functions)
number = 6 # Rebind 6 in the scope and see how it affects the results.
assert all(fun() == 6 for fun in functions) 

So the solution to this, of course, is to make a new enclosing scope for each number you want to bind. In Python, you can create new enclosing scopes with modules, classes, and functions. It's common to use a function just to create new enclosing scope for another function.

因此,解决这个问题的方法,当然是,为你想要绑定的每个数字创建一个新的封闭范围。在Python中,您可以使用模块、类和函数创建新的封闭作用域。通常使用一个函数来为另一个函数创建新的封闭范围。

In Python, a closure is a function that returns another function. Kind of like a function constructor. Check out get_fun in the following example:

在Python中,闭包是返回另一个函数的函数。有点像函数构造函数。在下面的示例中查看get_fun:

def get_fun(value):
    """:return: A function that returns :param:`value`."""
    def fun(): # Bound to get_fun's scope
        return value
    return fun

functions = []
for number in range(1, 6):
    functions.append(get_fun(number))
assert [fun() for fun in functions] == range(1, 6)

Since get_fun is a function, it gets to have its own internal scope. Every time you call get_fun with a value, a little table is created to keep track of bindings within it; i.e. it says, "Within this scope, the value identifier points to the thing that was passed." That scope goes away at the end of the function execution, unless there's a reason for it to hang around.

因为get_fun是一个函数,所以它有自己的内部作用域。每次调用get_fun时,都会创建一个小表来跟踪其中的绑定;也就是说,“在这个范围内,值标识符指向传递的对象。”这个作用域在函数执行的末尾消失,除非有理由让它继续存在。

If you're returning a function from within a scope, that's a good reason for parts of the "scope table" to hang around -- that function you're returning could reference things from that scope table when you call it later on. For that reason, when fun is created within get_fun Python tells fun about get_fun's scope table, which fun keeps handy for when it's needed.

如果您要从范围内返回一个函数,这是“范围表”的部分要挂起的一个很好的理由——您要返回的函数可以在稍后调用该范围表时引用该范围表中的内容。出于这个原因,当在get_fun Python中创建fun时,它会告诉fun关于get_fun的范围表,当需要时,这个表就很方便。

You can read more about the details and technical terminology (which I softened a bit) in the Python docs on the execution model. You can also look at the parts of the enclosing scope that a function refers to with print fun.__closure__. In the above, we see the reference to the value, which happens to be an int:

您可以在执行模型上阅读更多关于Python文档中的细节和技术术语(我稍微软化了一点)。您还可以查看函数在print fun.__closure__中引用的封闭范围的部分。在上面,我们看到了对值的引用,它恰好是一个int:

# Same as before, commented out for emphasis.
#functions = []
#for number in range(1, 6):
#    functions.append(get_fun(number))
#assert [fun() for fun in functions] == range(1, 6)
print functions[0].__closure__
# Produces: (<cell at 0x8dc30: int object at 0x1004188>,)

#4


2  

listOfLambdas = [lambda i=i: square(i) for i in listOfNumbers]

Or

listOfLambdas = map(lambda i: lambda: square(i), listOfNumbers)

#5


1  

Try to use () instead of []:

试着用()代替[]:

listOfLambdas = (lambda: square(i) for i in listOfNumbers)

And you will get:

你将得到:

1
4
9
16
25

#6


0  

I sometimes find that defining actual classes for function objects makes it easier to understand what's going on:

我有时发现,为函数对象定义实际的类可以更容易地理解发生了什么:

>>> class square(object):
...   def __init__(self, val):
...     self.val = val
...   def __call__(self):
...     return self.val * self.val
...
>>> l = [1,2,3,4,5]
>>> funcs = [square(i) for i in l]
>>> for f in funcs:
...   print f()
...
1
4
9
16
25
>>>

Granted, it's a bit more verbose than using lambdas or closures, but I find this easier to understand when I'm trying to do non-obvious things with functions.

当然,它比使用lambda或闭包要冗长一些,但是当我尝试用函数做不明显的事情时,我发现这更容易理解。

#7


0  

You could also do:

你也可以做的事:

>>> def squares():
...     for i in [1,2,3,4,5]:
...         yield lambda:i*i
... 
>>> print [square() for square in squares()]
[1, 4, 9, 16, 25]

#8


0  

As an additional comment, I would like to outline the possibility to generate lists of lambda functions from sympy matrices (I don't know if it is the best way to do it, but this is how I do and I find it convenient):

作为补充说明,我想概述一下从症状矩阵生成lambda函数列表的可能性(我不知道这是否是最好的方法,但我就是这么做的,我觉得很方便):

import sympy as sp
sp.var('Ksi')
# generate sympy expressions for Berstein's polynomials
B_expr = sp.Matrix([sp.binomial(3, i) * Ksi**i * (1 - Ksi)**(3-i) for i in range(4)])
# lambdify them 
B = [sp.lambdify((Ksi), B_expr[i]) for i in range(4) ]