2009-01-17 170 views
24

我想从Python中的常量列表创建一个lambda对象列表;例如:如何创建Python lambdas列表(在列表理解/ for循环中)?

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

这将创建拉姆达对象的名单,但是,当我运行它们:

for f in listOfLambdas: 
    print f(), 

我希望它会打印

1 4 9 16 25 

相反,它打印:

25 25 25 25 25 

似乎所有的lambda都被赋予了错误的参数。我做错了什么,有没有办法解决它?我在想Python 2.4。

编辑:更多的尝试新事物了一下,这样的想出了这一点:

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

打印预期广场从1到25,但是如果使用较早的print语句:

for f in listOfLambdas: 
    print f(), 

仍然给我所有的25 s。这两个打印调用之间现有的lambda对象是如何变化的?

相关问题:Why results of map() and list comprehension are different?

回答

17

我猜你创建的列表理解拉姆达被绑定到变量i最终在5结束了。因此,当你在事后评估lambda表达式,他们都被绑定到5并最终计算25.同样的事情发生在第二个例子中的num。当你在循环中评估lambda时,它的num没有改变,所以你得到正确的值。循环后,num是5 ...

我不太清楚你要做什么,所以我不知道如何提出解决方案。这个怎么样?

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

这给了我所期望的输出:

1 
4 
9 
16 
25 

另一种方式去思考的,这是一个拉姆达“捕获”它在那里创建点词法环境。所以,如果你给它num它实际上并没有解决这个值,直到它被调用。这既困惑又强大。

0

我有时会发现定义函数对象的实际类可以更容易地理解这是怎么回事:

>>> 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 
>>> 

诚然,这比使用Lambda表达式或倒闭更详细一点,但我觉得这是比较容易理解当我试图用函数做非显而易见的事情时。

18

您有:

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

for f in listOfLambdas: 
    print f() 

输出:

25 
25 
25 
25 
25 
25 

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

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

for f in listOfLambdas: 
    print f() 

输出:

0 
1 
4 
9 
16 
25 

注意i=i。这就是魔术发生的地方。

+2

很酷。这是“黑客”记录在任何地方?有没有更好的方法来做咖喱? 另外,请不要再提你的旋转床。 – 2009-01-17 02:02:50

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

或者

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

当函数语句执行就必然要他们(词汇)封闭的范围。

在您的代码片段中,lambda表达式绑定到全局作用域,因为for套件不作为Python中的独立作用域单元执行。在for循环结束时,num被绑定在封闭范围内。演示:

for num in range(1, 6): 
    pass 
assert num == 5 # num is now bound in 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 

同样的协议为列表解析:

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

令人兴奋的,我知道。任何人,凭借我们新发现的知识,我们可以确定您创建的lambda表达式是指在封闭范围内绑定的(单个)标识符。这应该使这个更有意义:

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) 

这里是一个演示它更最酷的部分:

# 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) 

所以解决这一点,当然,是让一个新的封闭范围为您要绑定的每个number。在Python中,您可以使用模块,类和函数创建新的封闭范围。通常只使用函数为另一个函数创建新的封闭作用域。

在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) 

由于get_fun是一个函数,它就会拥有自己的内部范围。每次您用值调用get_fun时,都会创建一个小表来跟踪其中的绑定;即它说:“在这个范围内,标识符指向已通过的事物。”这个范围在函数执行结束时会消失,除非有理由让它停下来。

如果你从一个范围内返回一个函数,那么这就是“范围表”部分存在的一个很好的原因 - 当你调用它时,你返回的函数可以引用该范围表中的东西稍后的。因此,在get_fun内创建fun时,Python会告诉funget_fun的范围表,其中fun在需要时会保持方便。

您可以在Python docs on the execution model中阅读更多关于细节和技术术语(我稍微软化了一下)的内容。您也可以通过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>,) 
1

尝试使用(:

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

,你会得到:

1 
4 
9 
16 
25 
0

你也可以这样做:

>>> 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] 
0

作为额外的评论,我想概述一下从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) ]