我以为我理解用于定义函数的lambda符号(在python中),但是我看到了一个我不理解的奇怪行为。我看了What do (lambda) function closures capture?并看到了一些我认为有关但却不一样的东西。
我在函数内生成一个函数列表。在该函数内部,列表中函数的行为与预期一致。然而,当它返回到main()时,列表的第1项到第3项都表现为第3项。我说“表现为”因为你可以看到函数实际上并不相同 - 它们存储在不同的内存中位置。
我添加了一些评论的调试行,这有助于了解代码的作用。 lambda函数依赖于一个整数,并且整数的值在函数内部是预期的,但对于main()中的列表项1到3是相同的。
如果有人能帮我理解这里发生了什么,我真的很感激。谢谢!
import numpy as np
def make_func_list(matA):
size = matA.shape[0]
vec = np.ones(size)
lst_f = []
lst_f.append( lambda x: sum(x) )
for ii in xrange(1,size):
lst_f.append( lambda x: np.dot(matA[ii], x) )
print '-----------------'
print '- in function -'
for ii in xrange(size):
print 'func_list', ii, lst_f[ii](vec)
print lst_f[ii] # note that the memory addresses are different
if ii > 0:
print lst_f[ii].func_closure[0].cell_contents # the integer is different
print '-----------------'
return lst_f
if __name__ == "__main__":
size = 4
matA = np.reshape(np.arange(size**2),(size,size))
vec = np.ones(size)
print "### matA ###"
print matA
func_list = make_func_list(matA)
print '================='
print '= in main() ='
for ii in xrange(len(matA)):
print 'func_list', ii, func_list[ii](vec)
print func_list[ii] # memory addresses are unchanged from in make_func_list()
if ii > 0:
print func_list[ii].func_closure[0].cell_contents # but the integer is the same for all ii
print '================='
答案 0 :(得分:1)
在这些方面:
for ii in xrange(1,size):
lst_f.append( lambda x: np.dot(matA[ii], x) )
创建的lambda对象存储对matA
和ii
变量的引用,并且当执行lambda时,它使用np.dot(matA[ii], x)
和matA
的值来评估ii
for
在调用lambda 时有,而不是在创建lambda时。不幸的是,lambdas在ii
循环运行完毕后被调用,此时size-1
的值将是分配给它的最后一个值(即ii
),所以所有lambdas的行为方式只会是最后一个lambda的行为。
有几种方法可以解决这个问题。一种方法是将for ii in xrange(1, size):
lst_f.append( (lambda ii_tmp: lambda x: np.dot(matA[ii_tmp], x))(ii) )
的当前值存储在只有当前lambda才能访问的范围内,这可以通过创建并立即调用另一个围绕“main”lambda的lambda来完成:
{{1}}
答案 1 :(得分:1)
每次循环时,lambda
表达式都会创建一个函数对象,该对象包含对ii
的引用,当函数被称为时,其值将被查找。由于所有lambda
都在同一范围内进行评估,因此它们都包含对同一名称 ii
的引用。分配给该名称的最后一个值来自循环的最后一次迭代,因此尽管函数对象是在循环的不同迭代中创建的,但每个将为ii
看到的值是相同的,即最后一个值这是从xrange(size)
获得的。
避免这种情况的一种简单(但稍微尴尬)的方法是将值ii
作为参数的默认值传递给函数对象,因为默认值是在定义时而不是调用时计算的。 / p>
lst_f.append( lambda x, _ii=ii: np.dot(matA[_ii], x) )