Comprehensions are having some unexpected interactions with scoping. Is this the expected behavior?
I\'ve got a method:
def leave_room(self, uid):
Yes, list comprehensions "leak" their variable in Python 2.x, just like for loops.
In retrospect, this was recognized to be a mistake, and it was avoided with generator expressions. EDIT: As Matt B. notes it was also avoided when set and dictionary comprehension syntaxes were backported from Python 3.
List comprehensions' behavior had to be left as it is in Python 2, but it's fully fixed in Python 3.
This means that in all of:
list(x for x in a if x>32)
set(x//4 for x in a if x>32) # just another generator exp.
dict((x, x//16) for x in a if x>32) # yet another generator exp.
{x//4 for x in a if x>32} # 2.7+ syntax
{x: x//16 for x in a if x>32} # 2.7+ syntax
the x
is always local to the expression while these:
[x for x in a if x>32]
set([x//4 for x in a if x>32]) # just another list comp.
dict([(x, x//16) for x in a if x>32]) # yet another list comp.
in Python 2.x all leak the x
variable to the surrounding scope.
UPDATE for Python 3.8(?): PEP 572 will introduce :=
assignment operator that deliberately leaks out of comprehensions and generator expressions! It's motivated by essentially 2 use cases: capturing a "witness" from early-terminating functions like any()
and all()
:
if any((comment := line).startswith('#') for line in lines):
print("First comment:", comment)
else:
print("There are no comments")
and updating mutable state:
total = 0
partial_sums = [total := total + v for v in values]
See Appendix B for exact scoping. The variable is assigned in closest surrounding def
or lambda
, unless that function declares it nonlocal
or global
.