对lambda和列表推导感到困惑
我之前在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
里面的内容当作一个块来处理,但它并没有这样做。通常情况下,这并不是个大问题,而且也很容易找到解决办法。