I answered a question here: comprehension list in python2 works fine but i get an error in python3
OP's error was using the same variables for max range and indices:
x = 12
y = 10
z = 12
n = 100
ret_list = [ (x,y,z) for x in range(x+1) for y in range(y+1) for z in range(z+1) if x+y+z!=n ]
This is a Python-3 error only, and related to the scopes that were added to the comprehension to avoid the variables defined here "leaking". Changing the variable names fixes that.
The error is:
UnboundLocalError: local variable 'y' referenced before assignment
because outer, global y
is shadowed by the local scope.
My question is: why do I get the error on y
and not on z
or x
?
EDIT: If I remove the loop on x
, the error moves to z
:
>> ret_list = [ (x,y,z) for y in range(y+1) for z in range(z+1) if x+y+z!=n ]
UnboundLocalError: local variable 'z' referenced before assignment
If I just do one loop:
ret_list = [ (x,y,z) for y in range(y+1) if x+y+z!=n ]
it works. So I'm suspecting that the first range
function is evaluated before all the other expressions, which leaves the value of x
intact. But the exact reason is still to be found. Using Python 3.4.3.
This behaviour is (implicitly) described in the reference documentation (emphasis mine).
This means that:
equivalent to:
As the name
x
in for the leftmost iterable is read in the enclosing scope as opposed to the nested scope then there is no name conflict between these two uses ofx
. The same is not true fory
, which is why it is where theUnboundLocalError
occurs.As to why this happens: a list comprehension is more-or-less syntactic sugar for
list(<generator expression>)
, so it's going to be using the same code path as a generator expression (or at least behave in the same way). Generator expressions evaluate the iterable expression in the leftmostfor
clause to make error handling when the generator expression somewhat saner. Consider the following code:y
is clearly the wrong type and so the addition will raise aTypeError
. By evaluatingrange(y + 1)
immediately that type error is raised on line 2 rather than line 3. Thus, it is easier to diagnose where and why the problem occurred. Had it occurred on line 3 then you might mistakenly assume that it was thex + 1
statement that caused the error.There is a bug report here that mentions this behaviour. It was resolved as "not a bug" for reason that it is desirable that list comprehensions and generator expressions have the same behaviour.