为什么这个简单的lambda函数会产生奇怪的输出?

时间:2017-11-08 10:28:20

标签: python lambda

我正在学习Python中的lambdas。我需要创建一个函数列表f = [f1, f2...],以便每个函数fi(x)都接收列表x并返回(x[i]-1)

这是我尝试编码的方式,但我得到了惊人的结果。请帮助我理解为什么三个打印件中的每一个都给出不同的结果。最后两个让我绝对难过!

f = [(lambda x: x[i]-1) for i in range(5)]
# I expect f to be [ (lambda x: x[0]-1), (lambda x: x[1]-1), ...]

x = [1, 2, 3, 4, 5]

print f[0](x), f[1](x), f[2](x)    # output: 4 4 4 !!
print [f[i](x) for i in range(5)]  # output: [0, 1, 2, 3, 4] as expected
print [f[k](x) for k in range(5)]  # output: [4, 4, 4, 4, 4] wha?!!!

编辑:这个问题与suggested duplicate完全不同。在链接的问题中,有一个简单的错误,用户创建了一个函数列表,而不是函数调用。然而,Tomasz Gandor的答案使用了几个例子来讨论与此处提出的问题相同的问题。

2 个答案:

答案 0 :(得分:6)

这是一个常见的问题。您正在寻找的是

f = [(lambda x, i=i: x[i]-1) for i in range(5)]
z = [1,2,3,4,5]
print f[0](z),f[1](z),f[2](z)
0 1 2

您的列表理解会创建5个函数,这些函数都指向i。在循环结束时,i等于4.因此,无论您调用什么lambda,它都会将i评估为4。

您希望将i的当前值分配给lambda函数的局部变量。您可以使用可选参数执行此操作。

Lambda在运行时评估params。 Python将允许你定义这样的东西,它可能会有更多的亮点:

>>> f = lambda x: x[i] - 1
>>> l = [1, 2, 3, 4]
>>> f(l)

Traceback (most recent call last):
  File "<pyshell#91>", line 1, in <module>
    f(l)
  File "<pyshell#89>", line 1, in <lambda>
    f = lambda x: x[i] - 1
IndexError: list index out of range

更新 :回复评论中的问题,此示例与原始问题的关联性较小,但有助于更好地了解函数索引评估的发生方式

myvar=1

def f(myvar=myvar):
    print myvar

f()  # prints 1
f(2) # prints 2
myvar = 5
f()  # prints 1

如果您在声明myvar之前未声明f,则会获得NameError: name 'myvar' is not defined。这是因为函数变量的评估是在&#39; compile&#39;时间。 Lambda对运行时的评估,但是当我们提供可选参数时 - 它会立即发生(因此i = i)。希望它提供更清晰的主题:))

答案 1 :(得分:3)

变量i在全局范围内,在调用任何函数时仍为4。如果您执行i = 1,那么您的两个异常输出将变为1 1 1[1 1 1 1 1]

我发现这有效:

g = lambda i: lambda x: x[i] - 1
f = [ g(i) for i in range(5) ]  # i missed here
print [f[k]([1,2,3,4,5]) for k in range(5)] # [0, 1, 2, 3, 4]

i现在的范围是第二个lambda。