I have a list and a lambda
function defined as
In [1]: i = lambda x: a[x]
In [2]: alist = [(1, 2), (3, 4)]
Then I try two different methods to calculate a simple sum
First method.
In [3]: [i(0) + i(1) for a in alist]
Out[3]: [3, 7]
Second method.
In [4]: list(i(0) + i(1) for a in alist)
Out[4]: [7, 7]
Both results are unexpectedly different. Why is that happening?
This behaviour has been fixed in python 3. When you use a list comprehension [i(0) + i(1) for a in alist]
you will define a
in its surrounding scope which is accessible for i
. In a new session list(i(0) + i(1) for a in alist)
will throw error.
>>> i = lambda x: a[x]
>>> alist = [(1, 2), (3, 4)]
>>> list(i(0) + i(1) for a in alist)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <genexpr>
File "<stdin>", line 1, in <lambda>
NameError: global name 'a' is not defined
A list comprehension is not a generator: Generator expressions and list comprehensions.
Generator expressions are surrounded by parentheses (“()”) and list comprehensions are surrounded by square brackets (“[]”).
In your example list()
as a class has its own scope of variables and it has access to global variables at most. When you use that, i
will look for a
inside that scope. Try this in new session:
>>> i = lambda x: a[x]
>>> alist = [(1, 2), (3, 4)]
>>> [i(0) + i(1) for a in alist]
[3, 7]
>>> a
(3, 4)
Compare it to this in another session:
>>> i = lambda x: a[x]
>>> alist = [(1, 2), (3, 4)]
>>> l = (i(0) + i(1) for a in alist)
<generator object <genexpr> at 0x10e60db90>
>>> a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> [x for x in l]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <genexpr>
File "<stdin>", line 1, in <lambda>
NameError: global name 'a' is not defined
When you run list(i(0) + i(1) for a in alist)
you will pass a generator (i(0) + i(1) for a in alist)
to the list
class which it will try to convert it to a list in its own scope before return the list. For this generator which has no access inside lambda function, the variable a
has no meaning.
The generator object <generator object <genexpr> at 0x10e60db90>
has lost the variable name a
. Then when list
tries to call the generator, lambda function will throw error for undefined a
.
The behaviour of list comprehensions in contrast with generators also mentioned here:
List comprehensions also "leak" their loop variable into the surrounding scope. This will also change in Python 3.0, so that the semantic definition of a list comprehension in Python 3.0 will be equivalent to list(). Python 2.4 and beyond should issue a deprecation warning if a list comprehension's loop variable has the same name as a variable used in the immediately surrounding scope.
In python3:
>>> i = lambda x: a[x]
>>> alist = [(1, 2), (3, 4)]
>>> [i(0) + i(1) for a in alist]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <listcomp>
File "<stdin>", line 1, in <lambda>
NameError: name 'a' is not defined