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

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

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

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

 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打印预期的方块,但使用之前的打印语句:

 for f in listOfLambdas: print f(), 

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

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

我猜测你在列表理解中创build的lambda是绑定到最终以5结束的variablesi的。因此,当你在事实之后评估lambda时,它们都被绑定到5,并最终计算25.在第二个例子中,同样的事情正在发生。 当你评估循环内的lambda时,它的num没有改变,所以你得到正确的值。 循环之后,num是5 …

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

 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 

另外一个想法是,lambda在它创build的地方“捕捉”它的词汇环境。 所以,如果你给它num,它实际上并没有解决这个值,直到它被调用。 这既困惑又强大。

你有:

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

输出:

 25 25 25 25 25 25 

你需要咖啡! 除了美味,使用这个默认值“黑客”。

 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 。 这就是魔术发生的地方。

当函数语句被执行时,它们被绑定到它们的(词法)封闭范围。

在你的代码片段中,lambdaexpression式绑定到全局范围,因为套件不是作为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 

列表parsing同样成立:

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

介意,我知道。 任何人,凭借我们新发现的知识,我们可以确定你正在创build的lambdaexpression式是指在封闭范围内绑定的(单个) 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) 

这是最酷的部分,它展示了更多:

 # 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创build一个新的封闭范围。 在Python中,可以使用模块,类和函数创build新的封闭范围。 使用函数只是为另一个函数创build新的封闭作用域是很常见的。

在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时,都会创build一个小表来跟踪其中的绑定; 即它说:“在这个范围内, value标识符指向已通过的东西。” 这个范围在函数执行结束的时候就会消失,除非有一个原因让它停下来。

如果你从一个范围内返回一个函数,那么这就是“范围表”部分的一个很好的原因 – 你返回的函数可以在稍后调用时引用这个范围表中的东西。 因此,在get_fun创buildfun时,Python会告诉您get_fun的作用域表的fun ,当需要的时候,这些fun会随时保持。

您可以在执行模型的Python文档中阅读关于细节和技术术语(我稍微软化了一下)的更多信息。 你也可以通过print fun.__closure__函数引用的封闭范围的部分。 在上面,我们看到了对value的引用,这恰好是一个整数:

 # 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>,) 
 listOfLambdas = [lambda i=i: square(i) for i in listOfNumbers] 

要么

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

尝试使用()而不是[]:

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

你会得到:

 1 4 9 16 25 

我有时会发现,为函数对象定义实际的类可以更容易理解正在发生的事情:

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

当然,这比使用lambdas或闭包稍微冗长一些,但是当我试图用函数做非显而易见的事情时,我发现这更容易理解。

你也可以这样做:

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

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