Nicolas Forstner
Nicolas Forstner

Reputation: 138

Can't acces a global variable inside a method

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

Answers (3)

Nicolas Forstner
Nicolas Forstner

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

shizhz
shizhz

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

vallentin
vallentin

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

Related Questions