如何创建 Python lambda 列表(在列表理解/for 循环中)?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/452610/
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
How do I create a list of Python lambdas (in a list comprehension/for loop)?
提问by Smashery
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.
似乎 lambda 表达式都被赋予了错误的参数。我做错了什么,有没有办法解决?我想我在 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 25
s. 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?
采纳答案by Dave Ray
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 结束。因此,当您事后评估 lambda 时,它们都绑定到 5 并最终计算25. 在你的第二个例子中,同样的事情发生在 num 上。当你在循环内评估 lambda 时,它的 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 numit doesn't actually resolve that value until its invoked. This is both confusing and powerful.
另一种思考方式是 lambda 在创建时“捕获”其词法环境。所以,如果你给它num ,它实际上不会解析该值,直到它被调用。这既令人困惑又强大。
回答by recursive
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.
注意i=i
. 这就是魔法发生的地方。
回答by cdleary
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:
在您的代码段中,lambda 绑定到全局范围,因为for
套件在 Python 中不是作为独立范围的单元执行的。在for
循环结束时,num
is 绑定在封闭范围内。演示:
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:
脑洞大开,我知道。任何人,凭借我们新发现的知识,我们可以确定您正在创建的 lambda 表达式是指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.
因此,解决方案当然是为每个number
要绑定的范围创建一个新的封闭范围。在 Python 中,您可以使用模块、类和函数创建新的封闭作用域。使用一个函数只是为另一个函数创建新的封闭作用域是很常见的。
In Python, a closureis 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
一个值时,都会创建一个小表来跟踪其中的绑定;即它说,“在这个范围内,value
标识符指向传递的东西。” 该作用域在函数执行结束时消失,除非有理由让它停留。
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.
如果您从作用域内返回一个函数,那么这就是“作用域表”的一部分闲置的一个很好的理由——当您稍后调用它时,您返回的那个函数可以引用该作用域表中的内容。出于这个原因,fun
在get_fun
Python 中创建的时间告诉fun
aboutget_fun
的范围表,这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__
。在上面,我们看到对 的引用value
,它恰好是一个 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>,)
回答by potar
Try to use () instead of []:
尝试使用 () 而不是 []:
listOfLambdas = (lambda: square(i) for i in listOfNumbers)
And you will get:
你会得到:
1
4
9
16
25
回答by jfs
listOfLambdas = [lambda i=i: square(i) for i in listOfNumbers]
Or
或者
listOfLambdas = map(lambda i: lambda: square(i), listOfNumbers)
回答by Jason Baker
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 或闭包更冗长,但我发现当我试图用函数做不明显的事情时,这更容易理解。
回答by Vaibhav Aggarwal
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]
回答by MarcoMag
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):
作为附加评论,我想概述从 sympy 矩阵生成 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) ]