为什么以下代码总是输出16?

时间:2009-12-22 09:45:52

标签: python

def makeActions():
    acts=[]
    for i in range(5):
        print len(acts)
        acts.append(lambda x: i ** x)
        print acts[i]
    return acts
acts=makeActions()
for i in range(5):
    print acts[i](2)

输出:

16
16
16
16
16

预期产出:

0
1
4
9
16

4 个答案:

答案 0 :(得分:23)

因为lambda中的i可能不是您所期望的。要验证这一点,请更改代码:

acts.append(lambda x: (i, i ** x))

现在print会告诉您i的价值:

(4, 16)
(4, 16)
(4, 16)
(4, 16)
(4, 16)

这意味着lambda不会复制i的值,而是保留对变量的引用,因此所有lambda都会看到相同的值。要解决此问题,请复制i

acts.append(lambda x, i=i: (i, i ** x))

i=ii内创建lambda的本地副本。

[编辑]现在为什么这样?在2.1之前的Python版本中,本地函数(即在其他函数内定义的函数)无法在同一范围内看到变量。

def makeActions():
    acts=[]
    for i in range(5):
        print len(acts)
        def f(x):   # <-- Define local function
            return i ** x
        acts.append(f)
        print acts[i]
    return acts

然后你会收到i未定义的错误。 lambda可以看到封闭的范围,代价是语法稍差。

此行为已在最新版本的Python(2.5,IIRC)中修复。使用这些旧版本的Python,您必须编写:

        def f(x, i=i):   # <-- Must copy i
            return i ** x

自修复(请参阅PEP 3104)以来,f()可以查看同一范围内的变量,因此不再需要lambda

答案 1 :(得分:6)

因为你创建的所有lambda函数都绑定到i,它在循环结束时变为4,并且我们都知道4 * 4 = 16

避免使用嵌套函数(闭包)创建函数,例如

def makePowerFunc(base):
    def powerFunc(x):
        return base**x
    return powerFunc

def makeActions():
    acts=[]
    for i in range(5):
        acts.append(makePowerFunc(i))

    return acts
acts=makeActions()
for i in range(5):
print acts[i](2)

<强>输出:

0
1
4
9
16

还有其他方法可以解决它,但最好有一个命名的嵌套函数而不是lambda,你可以用这样的闭包做更多的事情

答案 2 :(得分:5)

这是违反直觉或至少不太常见的语法。我想你的意思是:

acts.append(lambda x, i = i: i ** x)

将输出:

0
1
4
9
16

FN。在你的版本中,

acts.append(lambda x, i: i ** x)

创建了lambda函数,但它们都引用了循环中的本地i,它停在i = 4,所以你的所有lambdas都说:lambda x: 4 ** x,因此

for i in range(5):
    print acts[i](2)

将打印所有16个。


FFN。关于破损lambda的博客文章:http://math.andrej.com/2009/04/09/pythons-lambda-is-broken/

答案 3 :(得分:3)

这种现象称为lambda绑定,请参阅What is "lambda binding" in Python?