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 execut
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:
a
's reference is being pushed onto the stack.a
So, the problem is that LOAD_FAST goes before STORE_FAST.
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.
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: