tags:

views:

60

answers:

4

Given the following code:

a = 0
def foo():
  # global a
  a += 1
foo()

When run, Python complains: UnboundLocalError: local variable 'a' referenced before assignment

However, when it's a dictionary...

a = {}
def foo():
  a['bar'] = 0
foo()

The thing runs just fine...

Anyone know why we can reference a in the 2nd chunk of code, but not the 1st?

+1  A: 

The question is one of update.

You cannot update a because it is not a variable in your function's local namespace. The update-in-place assignment operation fails to update a in place.

Interestingly, a = a + 1 also fails.

Python generates slightly optimized code for these kind of statements. It uses a "LOAD_FAST" instruction.

  2           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        

Note that the use of a on left and right side of the equal sign leads to this optimization.

You can, however, access a because Python will search local and global namespaces for you.

Since a does not appear on the left side of an assignment statement, a different kind of access is used, "LOAD_GLOBAL".

  2           0 LOAD_CONST               1 (0)
              3 LOAD_GLOBAL              0 (a)
              6 LOAD_CONST               2 ('bar')
              9 STORE_SUBSCR        
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE        
S.Lott
+2  A: 

The difference is that in the first example you are assigning to a which creates a new local name a that hides the global a.

In the second example you are not making an assignment to a so the global a is used.

This is covered in the documentation.

A special quirk of Python is that – if no global statement is in effect – assignments to names always go into the innermost scope.

Mark Byers
I wonder why they decided to describe it as a 'special quirk'? Contrast with javascript where `a = 1` automatically assigns to global scope! The only good thing about that is that you can quickly recognize garbage code by the absences of `var a = 1`
aaronasterling
A: 

a += 1 is equivalent to a = a + 1. By assigning to variable a, it is made local. The value you attempt to assign a + 1 fails because a hasn't been bound yet.

Jeff M
A: 

That's a very common Python gotcha: if you assign to a variable inside a function (as you do, with +=), anywhere at all (not necessarily before you use it some other way), it doesn't use the global one. However, since what you're doing is effectively "a = a + 1", you're trying to access a (on the right-hand side of the expression) before assigning to it.

Try using global a at the beginning of your function (but beware that you'll overwrite the global a value).

On your second example, you're not assigning the the variable a, but only to one of its items. So the global dict a is used.

rbp