Question

I understand the concept of local and global variables in Python, but I just have a question about why the error comes out the way it is in the following code. Python execute the codes line by line, so it does not know that a is a local variable until it reads line 5. Does Python go back one line and tag it as an error after it tries to execute line 5?

a=0

def test():
    print a  #line 4, Error : local variable 'a' referenced before assignment
    a=0      #line 5

test()
Was it helpful?

Solution

Setup and Testing

To analyze your question, let's create two separate test functions that replicate your issue:

a=0

def test1():
    print(a)

test1()

prints 0. So, calling this function is not problematic, but on the next function:

def test2():
    print(a)  # Error : local variable 'a' referenced before assignment
    a=0  

test2()

We get an error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in test2
UnboundLocalError: local variable 'a' referenced before assignment

Disassembly

We can disassemble the two functions (first Python 2):

>>> import dis
>>> dis.dis(test1)
  2           0 LOAD_GLOBAL              0 (a)
              3 PRINT_ITEM          
              4 PRINT_NEWLINE       
              5 LOAD_CONST               0 (None)
              8 RETURN_VALUE        

And we see that the first function automatically loads the global a, while the second function:

>>> dis.dis(test2)
  2           0 LOAD_FAST                0 (a)
              3 PRINT_ITEM          
              4 PRINT_NEWLINE       

  3           5 LOAD_CONST               1 (0)
              8 STORE_FAST               0 (a)
             11 LOAD_CONST               0 (None)
             14 RETURN_VALUE      

seeing that a is assigned inside it, attempts to LOAD_FAST from the locals (as a matter of optimization, as functions are pre-compiled into byte-code before running.)

If we run this in Python 3, we see nearly the same effect:

>>> test2()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in test2
UnboundLocalError: local variable 'a' referenced before assignment
>>> 
>>> import dis
>>> dis.dis(test1)
  2           0 LOAD_GLOBAL              0 (print) 
              3 LOAD_GLOBAL              1 (a) 
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
              9 POP_TOP              
             10 LOAD_CONST               0 (None) 
             13 RETURN_VALUE     

>>> dis.dis() # disassembles the last stack trace
  2           0 LOAD_GLOBAL              0 (print) 
    -->       3 LOAD_FAST                0 (a) 
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
              9 POP_TOP              

  3          10 LOAD_CONST               1 (0) 
             13 STORE_FAST               0 (a) 
             16 LOAD_CONST               0 (None) 
             19 RETURN_VALUE        

We see our error is on the LOAD_FAST again.

OTHER TIPS

Python doesn't execute line by line in the function code you submitted. It has first to parse it as a bloc of execution. It decides if a variable is local or global depending if it is written at (function) local level. As it is the case, it decides that the variable is local, hence the error.

This is because, in python, you need to tell that you are going to modify the value of a global variable. You do that as:

def test():
  global a
  print a  #line 4, Error : local variable 'a' referenced before assignment
  a=0      #line 5

With global a, you can modify variables in the function. You may want to have a look at this:

Short explanation:

a variable is bound to the test function. When you attempt to print it, Python throws the error because the local variable a will be initialized later. But if you remove the a=0, the code will execute without any problems and output 0.

Longer explanation

Python interpreter starts searching for the variable named a from the local scope. It sees that a is indeed declared within the function and initialized only on the fifth line. Once it's found, the lookup is over.

When it tries to process print a (forth line) it says 'Oh boy, I need to print a variable that's not initialized yet, I'd better throw an error.'

Explanation based on function's bytecode

If you run this code:

import dis

a = 0

def test():
  print a  #line 4, Error : local variable 'a' referenced before assignment
  a = 0    #line 5

dis.dis(test)

you'll get this output:

  6           0 LOAD_FAST                0 (a)
              3 PRINT_ITEM          
              4 PRINT_NEWLINE       

  7           5 LOAD_CONST               1 (0)
              8 STORE_FAST               0 (a)
             11 LOAD_CONST               0 (None)
             14 RETURN_VALUE        

Explanation on the unclear parts of the above:

  • LOAD_FAST means that a's reference is being pushed onto the stack.
  • STORE_FAST means that Python assigns 0 (from LOAD_CONST) to the local variable a

So, the problem is that LOAD_FAST goes before STORE_FAST.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top