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

use*_*729 4 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)
Run Code Online (Sandbox Code Playgroud)

输出:

16
16
16
16
16
Run Code Online (Sandbox Code Playgroud)

预期产量:

0
1
4
9
16
Run Code Online (Sandbox Code Playgroud)

Aar*_*lla 23

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

acts.append(lambda x: (i, i ** x))
Run Code Online (Sandbox Code Playgroud)

现在print告诉你价值i:

(4, 16)
(4, 16)
(4, 16)
(4, 16)
(4, 16)
Run Code Online (Sandbox Code Playgroud)

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

acts.append(lambda x, i=i: (i, i ** x))
Run Code Online (Sandbox Code Playgroud)

小小的内部i=i创建了一个本地副本.ilambda

[编辑]现在为什么这样?在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
Run Code Online (Sandbox Code Playgroud)

然后你会得到一个i未定义的错误.lambda可以看到封闭的范围,代价是一种有点奇怪的语法.

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

        def f(x, i=i):   # <-- Must copy i
            return i ** x
Run Code Online (Sandbox Code Playgroud)

由于修复(参见PEP 3104),f()可以看到相同范围内的变量,因此lambda不再需要.


Anu*_*yal 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)
Run Code Online (Sandbox Code Playgroud)

输出:

0
1
4
9
16
Run Code Online (Sandbox Code Playgroud)

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


mik*_*iku 5

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

acts.append(lambda x, i = i: i ** x)
Run Code Online (Sandbox Code Playgroud)

这将输出:

0
1
4
9
16
Run Code Online (Sandbox Code Playgroud)

FN.在你的版本中,

acts.append(lambda x, i: i ** x)
Run Code Online (Sandbox Code Playgroud)

拉姆达函数创建,但他们都提到了本地i自循环,它停在i = 4,所以所有的lambda表达式都在说:lambda x: 4 ** x,因此

for i in range(5):
    print acts[i](2)
Run Code Online (Sandbox Code Playgroud)

将打印所有16s.


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