2

Global variables cannot be accessed within a function without using the global keyword; (fine) but I did not expect the following:

Case 1:

a = 1

def f():
  a += 1
  print(a)

>>> f()
...
UnboundLocalError: local variable 'a' referenced before assignment

Reason I presume: Function could not find variable a in its local scope.

Case 2:

a = 1

def f():
  print(a)

>>> f()
1

But now, the function finds variable a in its local scope. Contradicts, the reason I presumed in the previous case.

Why is this happening?

Pranjal Mittal
  • 10,772
  • 18
  • 74
  • 99
  • 6
    This is already covered in [FAQs](http://docs.python.org/2/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value). – Ashwini Chaudhary Dec 08 '13 at 17:36

1 Answers1

1

This is because for your second example, the compiler tries to help you out with a bit of magic. In the case of

def f():
  print(a)

Here's the bytecode that it reduces to:

dis.dis(f)
  2           0 LOAD_GLOBAL              0 (print) 
              3 LOAD_GLOBAL              1 (a) #aha!
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
              9 POP_TOP              
             10 LOAD_CONST               0 (None) 
             13 RETURN_VALUE

You can see that since you never assigned to a within the scope of f, the compiler knows you must be referring to a global.

When you start assigning to a, now the compiler won't try to help you out. Unless you've explicitly told it that a is a global, it will treat it as a local.

def g():
    x += 1

dis.dis(g)
  2           0 LOAD_FAST                0 (x) #note no assumption that it is global
              3 LOAD_CONST               1 (1) 
              6 INPLACE_ADD          
              7 STORE_FAST               0 (x) 
             10 LOAD_CONST               0 (None) 
             13 RETURN_VALUE  

And now you can ask the question, okay, but why does the compiler not help you out in your first example? One way of explaining it is of course "explicit is better than implicit". Though of course the compiler is implicitly doing stuff in your second example, so maybe that's not a satisfying explanation :).

Mostly it comes down to This Is How Python Works, I'd say. If you assign to a variable, python will treat it as local to that scope unless you tell it otherwise. So your statement:

Global variables cannot be accessed within a function without using the global keyword

is not quite correct. You can access variables from an outer scope but you cannot assign to said variables without explicitly declaring you want to.


Sidenote, this is perfectly legal:

x = [1]

def f():
    x[0] += 1

f()
#x is now [2]

Which may be confusing :-). This is because in this context, you're not assigning anything over the reference that x holds; the += operator actually invokes the __setattr__ method of lists in order to alter an attribute. In python 2 this trick is frequently used as a workaround for the fact that there is no nonlocal keyword.

roippi
  • 25,533
  • 4
  • 48
  • 73