1

I have the following simple code:

i = 1
j = 6
test = 99

def main():
    ### i = 1 ### uncommenting this solves the problem with i  
    while i <= j:              
        print("i: ", i, "test: ", test)                                                                      
        i += 2

main()

Error:

while i <= j : UnboundLocalError: local variable 'i' referenced before assignment

OK, so inserting the line defining i inside the function would solve this.

But WHY are the variables j and test not affected by this behaviour?
It seems to be special to the first variable of the while loop.

RubberBee
  • 150
  • 9

2 Answers2

1

When python compiles a function, it tracks all of the variables used on the left side of an equal sign (that is, all variables that have something assigned to them) and uses those as the set of local variables. Variables that are referenced but not assigned to are obviously not local variables and must be in the enclosing scope.

If you want to assign a value to a global variable, you have to tell python to break its local variables rule. You do that with the global keyword in a function. It tells python that for that one function only, the named variables are global and should not be compiled as local.

The reason i is a problem but test and j are not, is that i is the only one that is assigned to.

EDIT

Digging a little deeper, python keeps a list of local variables and calls those entries "slots". When the function is compiled, the variable name is converted into an index into the slot table. A name lookup turns into a simple integer lookup, making the function run faster. When the function is called, python creates a new slot table for the known local variables, initialized to "not assigned". When a variable is used, python checks its slot, sees its marked "not assigned" and raises the error.

tdelaney
  • 73,364
  • 6
  • 83
  • 116
  • 1
    Why is the error thrown on `while i <= j:`, though? Does the parser backtrack from the assignment to find the first reference of the name that threw the error? – roganjosh Feb 27 '20 at 08:39
  • @roganjosh You can access the values of global variables without declaring them as `global` in the function like `j` and `test`, but you need the deceleration if you want to change the value, like the OP is doing with `i`. – Guy Feb 27 '20 at 08:41
  • 1
    @roganjosh - python made `i` a local variable when it compiled the function on first load because it was assigned to in the function. This happened before the function was ever called. You tried to use the local variable `i` before you assigned anything to it, so boom. – tdelaney Feb 27 '20 at 08:42
  • @Guy sure, I understand the overall issue, but I didn't get why the error was thrown on the specific line vs. actually on the line making the assignment – roganjosh Feb 27 '20 at 08:47
  • @roganjosh when `while i <= j:` is executed, you are trying to get an object from `i` and compare it to the object in `j`. But no object has been put in `i`, not even a `None`. Python doesn't automatically assign a value to local variables but it does track whether the slot used to hold the local variable has something in it. – tdelaney Feb 27 '20 at 08:50
  • @roganjosh My guess is the interpreter is smart enough to recognize all the uses of the problematic variable. Removing `i += 2` will remove the error (although it will cause an infinite loop). – Guy Feb 27 '20 at 08:51
  • @roganjosh I added an update to the answer that hopefully explains it. – tdelaney Feb 27 '20 at 08:56
  • @tdelaney it does, thanks :) – roganjosh Feb 27 '20 at 08:57
0

Try this :

i = 1
j = 6
test = 99

def main():
    ### i = 1 ### uncommenting this solves the problem with i  
    global i
    while i <= j:              
        print("i: ", i, "test: ", test)                                                                      
        i += 2

main()
Prashant Kumar
  • 2,057
  • 2
  • 9
  • 22
  • 3
    This doesn't answer the question. "But WHY are the variables "j" and "test" not affected by this behaviour?" and you've just made them `global` when they don't need to be. It doesn't delineate the behaviour that the OP is trying to understand – roganjosh Feb 27 '20 at 08:50