简体   繁体   English

列表理解中的Lambdas在调用时返回lambda

[英]Lambdas from a list comprehension are returning a lambda when called

I am trying to iterate the lambda func over a list as in test.py , and I want to get the call result of the lambda, not the function object itself. 我试图在test.py的列表上迭代lambda func,我想得到lambda的调用结果,而不是函数对象本身。 However, the following output really confused me. 但是,以下输出真的让我很困惑。

------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>

I modified the variable name when print the call result to t as following, and everything goes well. 我将调用结果打印到t时修改了变量名,如下所示,一切顺利。 I am wondering what is all about of that. 我想知道这是怎么回事。 ?

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

-----------output-------------
4
4
4
4
4

In Python 2 list comprehension 'leaks' the variables to outer scope: 在Python 2中,列表理解“泄漏”变量到外部范围:

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

Note that the behavior is different on Python 3: 请注意,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

When you define lambda it's bound to variable i , not its' current value as your second example shows. 当你定义lambda时,它绑定到变量i ,而不是它的第二个例子显示的当前值。 Now when you assign new value to i the lambda will return whatever is the current value: 现在,当您为i分配新值时,lambda将返回当前值:

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

Since the value of i within the loop is the lambda itself you'll get it as a return value: 由于循环中i的值是lambda本身,因此您将获得它作为返回值:

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

UPDATE : Example on Python 2.7: 更新 :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>

Same on Python 3.4: 在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

For details about the change regarding the variable scope with list comprehension see Guido's blogpost from 2010 . 有关列表理解的变量范围变化的详细信息,请参阅2010年的 Guido 博客文章

We also made another change in Python 3, to improve equivalence between list comprehensions and generator expressions. 我们还在Python 3中进行了另一项更改,以改进列表推导和生成器表达式之间的等效性。 In Python 2, the list comprehension "leaks" the loop control variable into the surrounding scope: 在Python 2中,列表推导将循环控制变量“泄漏”到周围的范围中:

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

However, in Python 3, we decided to fix the "dirty little secret" of list comprehensions by using the same implementation strategy as for generator expressions. 但是,在Python 3中,我们决定使用与生成器表达式相同的实现策略来修复列表推导的“脏小秘密”。 Thus, in Python 3, the above example (after modification to use print(x) :-) will print 'before', proving that the 'x' in the list comprehension temporarily shadows but does not override the 'x' in the surrounding scope. 因此,在Python 3中,上面的例子(修改后使用print(x):-)将打印'before',证明列表理解中的'x'暂时阴影但不覆盖周围的'x'范围。

Closures in Python are late-binding , meaning that each lambda function in the list will only evaluate the variable i when invoked, and not when defined. Python中的闭包是后期绑定的 ,这意味着列表中的每个lambda函数只会在调用时评估变量i ,而不是在定义时。 That's why all functions return the same value, ie the last value of ì (which is 4). 这就是为什么所有函数返回的值相同,即最后一个值ì (也就是4)。

To avoid this, one technique is to bind the value of i to a local named parameter: 为避免这种情况,一种技术是将i的值绑定到本地命名参数:

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

Another option is to create a partial function and bind the current value of i as its parameter: 另一种选择是创建一个部分函数并将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

Edit: Sorry, misread the question initially, since these kind of questions are so often about late binding (thanks @soon for the comment). 编辑:对不起,最初误读了这个问题,因为这类问题经常与后期绑定有关(感谢@soon评论)。

The second reason for the behavior is list comprehension's variable leaking in Python2 as others have already explained. 这种行为的第二个原因是列表理解的变量在Python2中泄漏,正如其他人已经解释过的那样。 When using i as the iteration variable in the for loop, each function prints the current value of i (for the reasons stated above), which is simply the function itself. 当使用i作为for循环中的迭代变量时,每个函数都会打印i的当前值(由于上述原因),这只是函数本身。 When using a different name (eg t ), functions print the last value of i as it was in the list comprehension loop, which is 4. 当使用不同的名称(例如t )时,函数打印i的最后一个值,就像在列表推导循环中那样,它是4。

lambda: i is an anonymous function with no arguments that returns i. lambda: i是一个匿名函数,没有返回i的参数。 So you are generating a list of anonymous functions, which you can later (in the second example) bind to the name t and invoke with () . 因此,您将生成一个匿名函数列表,您可以稍后(在第二个示例中)绑定到名称t并使用()调用。 Note you can do the same with non-anonymous functions: 请注意,您可以对非匿名函数执行相同的操作:

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

@plamut has just answered the implied other part of the question, so I won't. @plamut刚刚回答了问题中隐含的其他部分,所以我不会。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM