Python 奇怪的行为:Lambda在列表中理解

Python 奇怪的行为:Lambda在列表中理解,python,lambda,list-comprehension,Python,Lambda,List Comprehension,在python 2.6中: [x() for x in [lambda: m for m in [1,2,3]]] 结果: [3, 3, 3] 我希望输出是[1,2,3]。即使使用非列表理解方法,我也会遇到完全相同的问题。即使我将m复制到另一个变量中 我错过了什么?我也注意到了。我的结论是lambda只创建了一次。事实上,你对内部列表的理解会给出3个相同的函数,都与m的最后一个值有关 尝试并检查元素的id() [注意:此答案不正确;请参阅注释]长话短说,您不想这样做。更具体地说,您遇到的是操

在python 2.6中:

[x() for x in [lambda: m for m in [1,2,3]]]
结果:

[3, 3, 3]
我希望输出是[1,2,3]。即使使用非列表理解方法,我也会遇到完全相同的问题。即使我将m复制到另一个变量中


我错过了什么?我也注意到了。我的结论是lambda只创建了一次。事实上,你对内部列表的理解会给出3个相同的函数,都与m的最后一个值有关

尝试并检查元素的id()


[注意:此答案不正确;请参阅注释]

长话短说,您不想这样做。更具体地说,您遇到的是操作顺序问题。您正在创建三个单独的
lambda
,它们都返回
m
,但没有一个会立即调用。然后,当你到达外部列表理解时,它们都被称为
m
的剩余值是3,内部列表理解的最后一个值

--征求意见--


同样,三个单独的ID。

为了让lambda记住
m
的值,可以使用一个带有默认值的参数:

[x() for x in [lambda m=m: m for m in [1,2,3]]]
# [1, 2, 3]

这是因为默认值在定义时设置一次。每个lambda现在使用自己的默认值
m
,而不是在lambda执行时在外部范围中查找
m
的值。

调用您遇到的效果,当您定义引用非局部变量的函数时,该函数保留对该变量的引用,而不是得到自己的副本。为了举例说明,我将把您的代码扩展成一个没有理解或lambdas的等效版本

inner_list = []
for m in [1, 2, 3]:
    def Lambda():
         return m
    inner_list.append(Lambda)
因此,此时,
internal_list
中有三个函数,调用每个函数时,将返回
m
的值。但最突出的一点是,他们都看到了非常相同的
m
,即使
m
正在发生变化,他们直到很久以后才看到它

outer_list = []
for x in inner_list:
    outer_list.append(x())

特别是,由于内部列表是在外部列表开始构建之前完全构建的,
m
已经达到了它的最后一个值3,并且所有三个函数都看到了相同的值。

查看函数的
闭包。所有3个都指向同一个单元格对象,该对象从外部范围保留对m的引用:

>>> print(*[x.__closure__[0] for x in [lambda: m for m in [1,2,3]]], sep='\n')
<cell at 0x00D17610: int object at 0x1E2139A8>
<cell at 0x00D17610: int object at 0x1E2139A8>
<cell at 0x00D17610: int object at 0x1E2139A8>

就我个人而言,我觉得这是一个更优雅的解决方案。Lambda返回一个函数,所以如果我们想使用这个函数,那么我们应该使用它。对于lambda中的'anonymous'变量和生成器,使用相同的符号是令人困惑的,因此在我的示例中,我使用了不同的符号,希望能让它更清楚

>>> [ (lambda a:a)(i) for i in range(3)]
[0, 1, 2]
>>> 
它也更快

>>> timeit.timeit('[(lambda a:a)(i) for i in range(10000)]',number=10000)
9.231263160705566
>>> timeit.timeit('[lambda a=i:a  for i in range(10000)]',number=10000)
11.117988109588623
>>> 
但速度不如地图:

>>> timeit.timeit('map(lambda a:a,  range(10000))',number=10000)
5.746963977813721

(我不止一次地运行了这些测试,结果是一样的,这是在python 2.7中完成的,在python 3中的结果是不同的:两个列表理解在性能上更接近,并且都慢了很多,map仍然快得多。)

@unubtu的答案是正确的。我用闭包在Groovy中重新创建了这个场景。也许这说明了正在发生的事情

这类似于[1,2,3]中的[x]的
[x()[lambda:m代表m]]

arr = []
x = 0
while (x < 3) {
  x++
  arr.add({ -> x })
}
arr.collect { f -> f() } == [3, 3, 3]
arr = []
x = 0
while (x < 3) {
  x++
  arr.add({_x -> { -> _x }}(x))
}
arr.collect { f -> f() } == [1, 2, 3]

请注意,如果我使用
[1,2,3],则不会发生这种情况。每个{x->…}
而不是while循环。Groovy while循环和Python列表理解都在迭代之间共享其闭包。

lambda不是一次创建的<代码>[lambda:m代表[1,2,3]]
将生成三个单独的lambda。是的,注意到了。抱歉:)id的开始和结束对我来说是一样的…检查lambda的id()。它们都是一样的,这意味着lambda持有对m的引用,而不是m的值。是否可以使lambda保存变量的值?谢谢@一般情况——我不知道,不知道,但是如果其他人知道一种方法,并且可以添加它作为附加评论,我很乐意记下它。除非它损害了您的最终结果,否则您使用生成器的方法是合理的,这将产生预期的输出:
[x()代表x英寸(lambda:m代表m英寸[1,2,3])
。。。但这适用于迭代器。>>>l=(lambda:m表示[1,2,3]中的m])>>>>[x()表示l中的x]这是因为生成器不会一次创建所有值,而是在请求时创建它们。列表理解和生成器表达式并不相同,尽管它们通常可以互换使用。有些情况下(如本例),行为明显不同。为什么
x()
不仅仅是
x
??它有什么不同呢???@amyassin-因为这个实例中的x是一个lambda(动态声明的匿名函数)。他正在调用
x()
来调用它。真的,你应该问你自己的问题。@g.d.d.c thanx,我需要知道在搜索过程中应该转向哪里……太棒了!没有想到为lambda设置默认值。棒极了。
>>> timeit.timeit('map(lambda a:a,  range(10000))',number=10000)
5.746963977813721
arr = []
x = 0
while (x < 3) {
  x++
  arr.add({ -> x })
}
arr.collect { f -> f() } == [3, 3, 3]
arr = []
x = 0
while (x < 3) {
  x++
  arr.add({_x -> { -> _x }}(x))
}
arr.collect { f -> f() } == [1, 2, 3]