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

2024-09-30 01:25:27 发布

您现在位置:Python中文网/ 问答频道 /正文

我以为我理解lambda符号(在python中)来定义函数,但是我看到了一个奇怪的行为,我不明白。我看了看What do (lambda) function closures capture?,看到了一些我认为相关但不相同的东西。你知道吗

我在一个函数中生成一个函数列表。在该函数中,列表中函数的行为与预期一样。但是,当它返回到main()时,列表中的项1到3的行为都与项3相同。我之所以说“behave as”,是因为可以看到函数实际上并不完全相同——它们存储在不同的内存位置。你知道吗

我放入了一些注释过的调试行,这有助于了解代码在做什么。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 '================='

Tags: lambda函数in列表forsizemainnp
2条回答

在这些行中:

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

创建的lambda对象存储对matAii变量的引用,当执行lambda时,它使用matAii在调用lambda时(而不是创建lambda时)具有的值来计算np.dot(matA[ii], x)。不幸的是,lambda是在for循环运行完毕后调用的,此时ii的值将是最后一个赋值给它的值(即size-1),因此所有lambda的行为方式都将是只期望最后一个lambda的行为方式。你知道吗

有几种方法可以解决这个问题。一种方法是将ii的当前值存储在只有当前lambda可以访问的范围内,这可以通过创建并立即调用围绕“main”lambda的另一个lambda来完成:

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

每次通过循环,lambda表达式都会创建一个function对象,其中包含对ii的引用,当调用函数时,将查找该引用的值。因为所有的lambda都在同一个范围内求值,所以它们都包含对同一名称ii的引用。分配给该名称的最后一个值来自循环的最后一次迭代,因此尽管函数对象是在循环的不同迭代中创建的,但是每个函数对象在ii中看到的值是相同的,即从xrange(size)获得的最后一个值。你知道吗

避免这种情况的一种简单(但有点尴尬)方法是将值ii作为参数的默认值传递给函数对象,因为默认值是在定义时计算的,而不是在调用时。你知道吗

lst_f.append( lambda x, _ii=ii: np.dot(matA[_ii], x) )

相关问题 更多 >

    热门问题