与lambda和列表理解混淆

时间:2012-02-09 05:21:13

标签: python lambda closures list-comprehension

使用以下语法

读回有关堆栈溢出的问题
In [1]: [lambda: x for x in range(5)][0]()
Out[1]: 4
In [2]: [lambda: x for x in range(5)][2]()
Out[2]: 4

但是我很难理解为什么这个输出恰好是4, 我的理解是它总是将列表的最后一个值作为输出,

In [4]: [lambda: x for x in [1,5,7,3]][0]()
Out[4]: 3

但仍不相信此语法如何以最后一个值结束。

如果我能对这种语法得到正确的解释,那将非常高兴

5 个答案:

答案 0 :(得分:15)

这不是关于列表推理还是lambdas。这是关于Python中的范围规则。让我们将列表理解重写为等效循环:

funcs = []
for x in range(5):
    def f(): return x
    funcs.append(f)
funcs[0]() # returns 4

在这里,我们可以看到我们连续构造函数并将它们存储在列表中。当调用其中一个函数时,所有发生的事情都是查找并返回x的值。但x是一个值变化的变量,因此4的最终值是总是返回的值。您甚至可以在循环后更改x的值,例如,

x = 32 
funcs[2]() # returns 32

为了获得您期望的行为,Python需要将for内容作为块进行范围化;它没有。通常,这不是问题,并且很容易解决。

答案 1 :(得分:4)

对于具有n次迭代的LC,x被分配源序列的元素0到n-1。在最后一次迭代中,x被赋予最后一个元素。需要注意的是,总是相同的x ,并且最后调用函数会返回最后持有的x

答案 2 :(得分:3)

让我打破我理解的代码

In [39]: [lambda: x for x in [1,5,7,3]]
Out[39]: 
[<function <lambda> at 0x2cd1320>,
 <function <lambda> at 0x2cd12a8>,
 <function <lambda> at 0x2cd10c8>,
 <function <lambda> at 0x2cd1050>]
上面的

给出了函数列表

In [40]: [lambda: x for x in [1,5,7,3]][1]
Out[40]: <function <lambda> at 0x2cd1488>

索引1从函数列表中提供1个函数。

现在这个函数将应用于x,它始终是list的最后一个值。多数民众赞成总是给出最后一个值。

就像下面的代码一样。

In [41]: [lambda: 2][0]()
Out[41]: 2


In [42]: alist = [1,5,7,3,4,5,6,7]
对于[1,5,7,3]中的x,x对等于函数f(x)。 和
lambda:x表示[1,5,7,3]中的x等于lambda:3

In [43]: def f(x):
   ....:     for x in alist:
   ....:         pass
   ....:     return x
In [44]: print f(alist)
7

答案 3 :(得分:1)

这将解决它:

[(lambda(i): lambda: i)(x) for x in range(5)][2]()

问题是你没有在列表推导的每次迭代中捕获x的,你每次都在捕获变量

答案 4 :(得分:1)

正如其他人所说,你遇到了常见的循环闭包问题;即闭包通过引用捕获变量的问题。在这种情况下,您将捕获一个值随时间变化的变量(与所有循环变量一样),因此它在运行时的值与创建时的值不同。

要完成您想要的任务,您需要在创建lambda时捕获变量的值。 sblom建议将其包装在一个立即执行的函数中的解决方案:

[(lambda(i): lambda: i)(x) for x in range(5)][2]()

您经常在Python中看到的另一个解决方案是使用默认参数,它还会在创建时评估值:

[lambda i=x: i for x in range(5)][2]()

(它通常写成lambda x=x: x,但为了清晰起见我重命名了变量)