Odd name shadowing in comprehension
Steve D'Aprano
steve+python at pearwood.info
Sat Oct 22 23:14:37 EDT 2016
On Sun, 23 Oct 2016 11:43 am, Terry Reedy wrote:
> On 10/22/2016 7:57 PM, Chris Angelico wrote:
>> This surprised me.
>>
>> Python 3.4.2 (default, Oct 8 2014, 10:45:20)
>> [GCC 4.9.1] on linux
>> Type "help", "copyright", "credits" or "license" for more information.
>>>>> y=6
>>>>> [(x,y) for x in range(y) for y in range(3)]
>> [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2,
>> 2), (3, 0), (3, 1), (3, 2), (4, 0), (4, 1), (4, 2), (5, 0), (5, 1),
>> (5, 2)]
>>>>> [(x,y) for x in range(3) for z in range(y) for y in range(3)]
>> Traceback (most recent call last):
>> File "<stdin>", line 1, in <module>
>> File "<stdin>", line 1, in <listcomp>
>> UnboundLocalError: local variable 'y' referenced before assignment
>>
>> Normally, a comprehension is described as being equivalent to an
>> unrolled loop, inside a nested function. That would be like this:
>>
>> def temp():
>> ret = []
>> for x in range(y):
>> for y in range(3):
>> ret.append((x,y))
>> return ret
>> temp()
>
> This would make the first example fail, which would not be nice.
>
>
>> But it seems that the first iterator (and only that one) is evaluated
>> in the parent context:
>
> Because the first iterator *can* always be evaluated.
I don't understand what you mean by that. If I take you literally, it is
obviously not true:
py> [x for x in garglebarblewarble]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'garglebarblewarble' is not defined
but I'm sure you know that, so I don't understand what you mean by "always".
According to the normal Python scoping rules[1], variables only come from a
single scope at a time. Lua has different rules: translating into Python,
Lua functions work like this:
x = 'global'
def foo():
print x # here, x will be the global x
x = 'local'
print x # but now it is the local x
and foo() will print "global" then "local". But according to Python's
scoping rules, foo must raise NameError, specifically
UnboundLocalError: local variable 'x' referenced before assignment
So it seems strange that a little bit of Lua's behaviour has crept into list
comprehensions. I doubt that's intentional.
There's definitely something strange going on. Compare the what happens when
the semi-global variable is in the first loop iterable versus the second
loop iterable. In this first example, y refers to both the global and the
local, yet strangely there's no error:
py> y = 999
py> [(y, z, x) for x in (1, y) for z in (10, 20) for y in (100,)]
[(100, 10, 1), (100, 20, 1), (100, 10, 999), (100, 20, 999)]
but if we move the reference to y into the second loop, the usual rule about
undefined local variables is used:
py> [(y, z, x) for x in (1, 2) for z in (10, y) for y in (100,)]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <listcomp>
UnboundLocalError: local variable 'y' referenced before assignment
Of course there's no problem with accessing globals in the second loop, so
long as the name doesn't clash with a local:
py> Y = 999
py> [(y, z, x) for x in (1, 2) for z in (10, Y) for y in (100,)]
[(100, 10, 1), (100, 999, 1), (100, 10, 2), (100, 999, 2)]
[1] Function declarations are *slightly* different, so we can write this:
def func(a, b=b)
to define a parameter (local variable) "b" that takes its default value from
b in the surrounding scope. But that's a declaration, not an expression.
--
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.
More information about the Python-list
mailing list