0
x = 2
y = 3

mylist = [ [x,y] for x in range(x+1) for y in range(y+1)]

print(mylist) 

UnboundLocalError Traceback (most recent call last) in 3 4 ----> 5 mylist = [ [x,y] for x in range(x+1) for y in range(y+1)] 6 7 print(mylist)

in (.0) 3 4 ----> 5 mylist = [ [x,y] for x in range(x+1) for y in range(y+1)] 6 7 print(mylist)

UnboundLocalError: local variable 'y' referenced before assignment

I do not understand what is wrong here and how to correct it.

petezurich
  • 9,280
  • 9
  • 43
  • 57
A.George
  • 15
  • 1
  • 5
  • 1
    Just in case you don't see it in the correct answer below, the problem is that you are using the variables `x` and `y` for two conflicting purposes: input to the loops, and the loop variables. Python can't straighten them out. – Tim Roberts Apr 15 '21 at 05:45
  • 1
    This is an interesting example of how scopes work for comprehensions – Mad Physicist Apr 15 '21 at 05:46

3 Answers3

3

This is because the CPython implementation of an inner comprehension in a multi-level comprehension is to make it a separate function, which has its own scope. By making y a left-value with its use as an iteration variable, y is determined to be a local variable to the separate function by the Python compiler, and as such the y in the range(y+1) cannot be referenced because the local y is not yet assigned a value in that function scope.

You can either rename the local y in the comprehension:

mylist = [[x, z] for x in range(x + 1) for z in range(y + 1)]

or use a non-comprehension solution instead:

mylist = []
for x in range(x + 1):
    for y in range(y + 1):
        mylist.append([x, y])
blhsing
  • 91,368
  • 6
  • 71
  • 106
  • Just renaming local `y` does not fix the problem entirely. OP has to change both local `x` and `y`. – petezurich Apr 15 '21 at 07:10
  • Your non-comprehension solution yields a different result than the intended one. – petezurich Apr 15 '21 at 07:14
  • 1
    Thanks. I've updated my answer with a corrected explanation and solution. `x` does not need to be renamed because it is assigned in the outermost level of the comprehension, which CPython's implementation does not turn into a function. – blhsing Apr 15 '21 at 07:20
  • 1
    Thanks too for your explanation regarding `x`! That's interesting, didn't know that. – petezurich Apr 15 '21 at 07:24
  • 2
    Small detail: Your non-comprehension solution still yields a slightly different result. The elements are correct but the order is not. – petezurich Apr 15 '21 at 07:27
  • OP is not expecting inner range to be evaluated in a different scope, so you need to change `y` to a different name except the range. – Mad Physicist Apr 15 '21 at 10:54
2

Normally, you see this error when you call a function that references a name in the local scope before assigning to it. As it happens, the outermost scope in a list comprehension works the same way in Python 3. This is a change from python 2, where loop variables in a comprehension would pollute the outer namespace.

In this particular case, using x and y as loop variables tells the interpreter that these are local variables. However, range(x + 1) and range(y + 1) attempt to read these variables before they are assigned. Python has an optimization to read variables that are determined to be local to a function (or comprehension) namespace from a special table rather than using normal LEGB lookup order. That is why you get the error instead of using the value from the outer scope.

A side-effect of this process is that the last value of x and y will not change the values that you assigned in the outer scope.

To fix this issue, ensure that the variables that define the range do not have the same name as the loop variables.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
1

Try this:

x = 2
y = 3

mylist = [[j,k] for j in range(x+1) for k in range(y+1)]

print(mylist)

Prints out:

[[0, 0], [0, 1], [0, 2], [0, 3], [1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3]]

As @Tim Roberts pointed out in his comment you are using x and y «for two conflicting purposes» in your list comprehension. You need to give unique names to each of them in the for loop, e.g. y -> k.

Have further further look at this SO post to learn more about nested list comprehensions.

petezurich
  • 9,280
  • 9
  • 43
  • 57