如何创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) ]