为什么lambda函数在生成它的函数中将其行为改变为main?

时间:2014-10-26 21:39:16

标签: python lambda

我以为我理解用于定义函数的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 '================='

2 个答案:

答案 0 :(得分:1)

在这些方面:

for ii in xrange(1,size):
    lst_f.append( lambda x: np.dot(matA[ii], x) )

创建的lambda对象存储对matAii变量的引用,并且当执行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) )