3

I am trying to better understanding scoping in python. I have the following toy example:

a = 1
print "in global: " + str(a)

def g():
    a += 1
    print "in g(): " + str(a)


def f():
    a += 1
    print "in f(): " + str(a)
    g()

f()

I expected this to run and print out 1 then 2 then 2 again. However, instead I get en error:

UnboundLocalError: local variable 'a' referenced before assignment

I would have thought both g() and f() would pull a from the global scope. Incorrect?

UPDATED: Thanks for the answers but what isn't clear is this: if I would like to just read the global variable a and assign it to a local variable that I create, also named a is that possible?

The reason I am doing this is I'm trying to figure out if g() inherits the scope of f() when it is called or the global scope where it is defined?

midori
  • 4,807
  • 5
  • 34
  • 62
Alex
  • 19,533
  • 37
  • 126
  • 195

4 Answers4

5

You are trying to change a in outer scope of functions, a is not in the scope of current functions, that's why you are getting such an error, because your functions know nothing about a. If you want to change a from within functions you need to use `global':

a = 1
print "in global: " + str(a)

def g():  
    global a
    a += 1
    print "in g(): " + str(a)


def f():
    global a
    a += 1
    print "in f(): " + str(a)
    g()

to print out the global a use it this way:

def f():
    print a
    new_a = a
    print new_a

There is a good example about global here

And if you want to use local variable a with value from global a use globals:

def g():  
    a = globals()['a']
    a += 1
    print "in g(): " + str(a)
midori
  • 4,807
  • 5
  • 34
  • 62
4

You need to use the global keyword.

>>> a = 1         
>>> def g():      
...     global a  
...     a += 1    
...     print a   
...               
>>> g()           
2    
ComputerFellow
  • 11,710
  • 12
  • 50
  • 61
4

You are missing the global operative in the function g and f like this:

a = 1

def g():
    global a
    a += 1
    print "in g(): " + str(a)

def f():
    global a
    a += 1
    print "in f(): " + str(a)

You can find more on global and local variables in the documentation here and from stack overflow here

Community
  • 1
  • 1
Michal Frystacky
  • 1,418
  • 21
  • 38
1

When you ask for a variable value, Python follows LEGB rule: local, enclosing, global, built-in.

But when you assign a variable, it is created from scratch in the local scope (assuming the variable assignment is inside function), discarding whatever value was previously bound to it:

a = 5
def f(x):
   print a  # Prints `5`, because name `a` can be found following LEGB rule in Global scope.
   print x  # Prints whatever value `x` was having, found following LEGB rule in Local scope.
   x = 123  # Forgets previous value of `x`, re-assign it to `123` in the Local scope.

Your problem with unbound variable appears here: a += 1 is simply a syntactic sugar for a = a + 1. When you do a = a + 1, Python catches that a is local , so it produces LOAD_FAST command in bytecode:

import dis
a = 1
def f():
  a += 1
print dis.dis(f)

          0 LOAD_FAST                0 (a)
          3 LOAD_CONST               1 (1)
          6 INPLACE_ADD
          7 STORE_FAST               0 (a)
         10 LOAD_CONST               0 (None)
         13 RETURN_VALUE

LOAD_FAST is used for local variable fetching. But you do not have a in local scope yet when LOAD_FAST is called, so the command fails.

Actually, if you do not assign to a, but want to modify it from local scope, you can do it via method calls. Unfortunately, int don't have modifying methods, so I'll give an example with class:

class Number(object):
    def __init__(self, x):
        self.x = x
    def add_one(self):
        self.x += 1

a = Number(1)
def f():
    a.add_one()
    print a.x


print a.x
f()
print a.x

1
2
2

Here you essentially do a=a+1, but in a more complicated manner. This is because calls to a are resolved as Global (and work fine) and you don't do any assignments inside f(). Method add_one is called on existing object - it does not care whether it was global or local in the caller.

Community
  • 1
  • 1
Mike Koltsov
  • 336
  • 3
  • 6