我试图在列表中迭代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>
Run Code Online (Sandbox Code Playgroud)
我在打印调用结果时修改了变量名t
,如下所示,一切顺利.我想知道这是怎么回事.?
--------test.py(update)--------
a = [lambda: i for i in range(5)]
for t in a:
print t()
-----------output-------------
4
4
4
4
4
Run Code Online (Sandbox Code Playgroud)
nie*_*mmi 46
在Python 2中,列表理解"泄漏"变量到外部范围:
>>> [i for i in xrange(3)]
[0, 1, 2]
>>> i
2
Run Code Online (Sandbox Code Playgroud)
请注意,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
Run Code Online (Sandbox Code Playgroud)
当你定义lambda时,它绑定到变量i
,而不是它的第二个例子显示的当前值.现在,当您i
为lambda 分配新值时,将返回当前值:
>>> a = [lambda: i for i in range(5)]
>>> a[0]()
4
>>> i = 'foobar'
>>> a[0]()
'foobar'
Run Code Online (Sandbox Code Playgroud)
由于i
循环内的值是lambda本身,因此您将获得它作为返回值:
>>> i = a[0]
>>> i()
<function <lambda> at 0x01D689F0>
>>> i()()()()
<function <lambda> at 0x01D689F0>
Run Code Online (Sandbox Code Playgroud)
更新: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>
Run Code Online (Sandbox Code Playgroud)
在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
Run Code Online (Sandbox Code Playgroud)
有关列表理解的变量范围变化的详细信息,请参阅2010年的 Guido 博客文章.
我们还在Python 3中进行了另一项更改,以改进列表推导和生成器表达式之间的等效性.在Python 2中,列表推导将循环控制变量"泄漏"到周围的范围中:
x = 'before'
a = [x for x in 1, 2, 3]
print x # this prints '3', not 'before'
Run Code Online (Sandbox Code Playgroud)
但是,在Python 3中,我们决定使用与生成器表达式相同的实现策略来修复列表推导的"脏小秘密".因此,在Python 3中,上面的例子(修改后使用print(x):-)将打印'before',证明列表理解中的'x'暂时阴影但不覆盖周围的'x'范围.
pla*_*mut 19
Python中的闭包是后期绑定的,这意味着列表中的每个lambda函数只会i
在调用时评估变量,而不是在定义时.这就是为什么所有函数都返回相同的值,即最后一个值ì
(即4).
为避免这种情况,一种技术是将值绑定i
到本地命名参数:
>>> a = [lambda i=i: i for i in range(5)]
>>> for t in a:
... print t()
...
0
1
2
3
4
Run Code Online (Sandbox Code Playgroud)
另一种选择是创建一个部分函数并将当前值绑定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
Run Code Online (Sandbox Code Playgroud)
编辑:对不起,最初误读了这个问题,因为这类问题经常与后期绑定有关(感谢@soon评论).
这种行为的第二个原因是列表理解的变量在Python2中泄漏,正如其他人已经解释过的那样.当i
在for
循环中用作迭代变量时,每个函数都会打印当前值i
(出于上述原因),这只是函数本身.当使用不同的名称(例如t
)时,函数打印i
列表推导循环中的最后一个值,即4.