Reputation: 138
I have a problem that when i try to use a global variable inside a method, an error is produced ("local variable 'b' referenced before assignment"). Why is this not the case when the variable is an element of a list?
this works fine:
a = [1]
def a_add():
a[0] += 1
a_add()
print(a)
but this doesn't:
b = 1
def b_add():
b += 1
b_add()
print(b)
Upvotes: 3
Views: 88
Reputation: 138
If anybody wants to take a deep dive into CPython and see the exact reason why this does not work, check out this link to the CPypthon ceval.c sourcecode file. This is the code that executes the python bytecode. As Vallentin said, python does a LOAD_FAST bytecode instruction for loading b (see below).
>>> x = 1
>>> def f():
... x += 1
...
>>> dis.dis(f)
2 0 LOAD_FAST 0 (x)
2 LOAD_CONST 1 (1)
4 INPLACE_ADD
6 STORE_FAST 0 (x)
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
In the ceval.c file on line 1228 (as of the 30st of May 2019) the relevant code is as follows:
case TARGET(LOAD_FAST): {
PyObject *value = GETLOCAL(oparg);
if (value == NULL) {
format_exc_check_arg(tstate, PyExc_UnboundLocalError,
UNBOUNDLOCAL_ERROR_MSG,
PyTuple_GetItem(co->co_varnames, oparg));
goto error;
Upvotes: 0
Reputation: 12531
The official FAQ page has detailed explanation for this error:
>>> x = 10
>>> def foo():
... print(x)
... x += 1
>>> foo()
Traceback (most recent call last):
...
UnboundLocalError: local variable 'x' referenced before assignment
This is because when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope. Since the last statement in foo assigns a new value to x, the compiler recognizes it as a local variable. Consequently when the earlier print(x) attempts to print the uninitialized local variable and an error results.
And for code:
a = [1]
def a_add():
a[0] += 1
a_add()
print(a)
It just reads value from and assigns value to the first slot of the global
array, so there's no problem.
Upvotes: 3
Reputation: 26235
When you try to assign something to b
Python does a LOAD_FAST
which is in relation to locals. You need to add global b
before trying to use b
.
def b_add():
global b
b += 1
From the other point of view of:
def b_add():
print(b)
Python instead does a LOAD_GLOBAL
which loads in relation to globals. Thus when you did a[0]
it first does LOAD_GLOBAL
for a
and then subsequently stores the value.
Upvotes: 2