对lambda和列表推导感到困惑

15 投票
5 回答
942 浏览
提问于 2025-04-17 12:38

我之前在Stack Overflow上看到一个问题,里面有这样的语法

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 个回答

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从这个函数列表中取出了一个函数。

现在这个函数会作用于x,而x总是列表中的最后一个值。所以y总是返回最后一个值作为结果。

就像下面的代码一样。

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


In [42]: alist = [1,5,7,3,4,5,6,7]

x在[1,5,7,3]中的意思是等同于下面的函数f(x)。

lambda: x在[1,5,7,3]中的意思是等同于lambda: 3。

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

对于一个有 n 次循环的情况,x 会被赋值为源序列中从 0 到 n-1 的元素。在最后一次循环中,x 会被赋值为最后一个元素。需要注意的是,始终使用的是同一个 x,所以在最后调用这个函数时,会返回 x 最后保存的值。

15

这段内容其实跟列表推导式和匿名函数没太大关系,主要是讲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里面的内容当作一个块来处理,但它并没有这样做。通常情况下,这并不是个大问题,而且也很容易找到解决办法。

撰写回答