来自列表理解的Lambdas在被调用时返回一个lambda

我想迭代在列表中的lambda func在test.py ,我想获得lambda的调用结果,而不是函数对象本身。 但是,下面的输出真的让我困惑。

 ------test.py--------- #!/bin/env python #coding: utf-8 a = [lambda: i for i in range(5)] for i in a: print i() --------output--------- <function <lambda> at 0x7f489e542e60> <function <lambda> at 0x7f489e542ed8> <function <lambda> at 0x7f489e542f50> <function <lambda> at 0x7f489e54a050> <function <lambda> at 0x7f489e54a0c8> 

打印电话结果到t时,我修改了variables名,如下,一切顺利。 我想知道这是什么。 ?

 --------test.py(update)-------- a = [lambda: i for i in range(5)] for t in a: print t() -----------output------------- 4 4 4 4 4 

在Python 2中,列表理解会将variables“泄漏”到外部作用域:

 >>> [i for i in xrange(3)] [0, 1, 2] >>> i 2 

请注意,Python 3的行为不同:

 >>> [i for i in range(3)] [0, 1, 2] >>> i Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'i' is not defined 

当你定义lambda时,它被绑定到variablesi ,而不是你的第二个例子所显示的当前值。 现在当你给i赋值时,lambda将返回当前值:

 >>> a = [lambda: i for i in range(5)] >>> a[0]() 4 >>> i = 'foobar' >>> a[0]() 'foobar' 

由于循环中的i值是lambda本身,所以将它作为返回值:

 >>> i = a[0] >>> i() <function <lambda> at 0x01D689F0> >>> i()()()() <function <lambda> at 0x01D689F0> 

更新 :Python 2.7的例子:

 Python 2.7.6 (default, Jun 22 2015, 17:58:13) [GCC 4.8.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> a = [lambda: i for i in range(5)] >>> for i in a: ... print i() ... <function <lambda> at 0x7f1eae7f15f0> <function <lambda> at 0x7f1eae7f1668> <function <lambda> at 0x7f1eae7f16e0> <function <lambda> at 0x7f1eae7f1758> <function <lambda> at 0x7f1eae7f17d0> 

在Python 3.4上也一样:

 Python 3.4.3 (default, Oct 14 2015, 20:28:29) [GCC 4.8.4] on linux Type "help", "copyright", "credits" or "license" for more information. >>> a = [lambda: i for i in range(5)] >>> for i in a: ... print(i()) ... 4 4 4 4 4 

有关列表理解的variables范围变化的详细信息,请参阅2010年 Guido的博客post 。

我们还在Python 3中做了另一个改进,以改善列表推导和生成器expression式之间的等价性。 在Python 2中,列表理解将循环控制variables“泄漏”到周围的范围中:

 x = 'before' a = [x for x in 1, 2, 3] print x # this prints '3', not 'before' 

然而,在Python 3中,我们决定使用与生成器expression式相同的实现策略来修复列表推导的“脏小秘密”。 因此,在Python 3中,上面的例子(使用print(x):-)进行修改之后将会打印'before',certificate列表理解中的'x'暂时阴影,但不会覆盖周围的'x'范围。

Python中的闭包是后期绑定的 ,这意味着列表中的每个lambda函数只会在调用时计算variablesi ,而不是在定义时计算。 这就是为什么所有函数返回相同的值,即最后一个值(这是4)。

为了避免这种情况,一种方法是将i的值绑定到本地命名参数:

 >>> a = [lambda i=i: i for i in range(5)] >>> for t in a: ... print t() ... 0 1 2 3 4 

另一种select是创build一个局部函数并绑定当前值i作为参数:

 >>> from functools import partial >>> a = [partial(lambda x: x, i) for i in range(5)] >>> for t in a: ... print t() ... 0 1 2 3 4 

编辑:对不起,最初误解了这个问题,因为这类问题往往是关于后期绑定(谢谢@soon的评论)。

行为的第二个原因是Python2中的列表理解的variables泄漏,正如其他人已经解释的那样。 当在for循环中使用i作为迭代variables时,每个函数都会打印出i的当前值(出于上述原因),这只是函数本身。 当使用不同的名称(例如t )时,函数将显示列表理解循环中的最后一个值, i 4。

lambda: i是一个匿名函数,没有返回i的参数。 因此,您正在生成一个匿名函数列表,您可以稍后(在第二个示例中)绑定到名称t并使用()调用。 注意你可以用非匿名函数来做同样的事情:

 >>> def f(): ... return 42 ... >>> name = f >>> name <function f at 0x7fed4d70fb90> >>> name() 42 

@plamut刚刚回答了这个问题的暗示的其他部分,所以我不会。