4

Possible Duplicate:
referenced before assignment error in python

I'm getting a strange error in python. The following ipython log sums it up:

In [10]: def confused(stuff):
   ....:     print huh
   ....:     return stuff
   ....: 

In [11]: confused(87)
0
Out[11]: 87

In [12]: def confused(stuff):
   ....:     print huh
   ....:     huh += 1
   ....:     return stuff
   ....: 

In [13]: confused(9)
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
/home/max/verk/btr-email/build/x86_64/bin/ipython in <module>()
----> 1 confused(9)

/home/max/verk/btr-email/build/x86_64/bin/ipython in confused(stuff)
      1 def confused(stuff):
----> 2     print huh
      3     huh += 1
      4     return stuff

UnboundLocalError: local variable 'huh' referenced before assignment

The only difference between the function that works and the one that throws an error is the +=1 line, and even then, it throws an error on a line which was previously working! It also doesn't throw an error if I put global huh before referencing huh in the 2nd version of the method.

Why does adding a line where I add one to the variable suddenly change it from a global to a local variable?

Community
  • 1
  • 1
mavix
  • 2,488
  • 6
  • 30
  • 52

1 Answers1

7

In your script, huh refers to a global variable. You can't change the reference to a global variable in a function without explicitly telling python that is what you want to do:

def confused(stuff):
    global huh
    print huh
    huh += 1
    return stuff

For immutable objects like ints, strings, floats, etc, that means that you can't make any changes to the object without declaring it as global. For mutable objects, you can make changes to the objects items or attributes, but you still can't change the object's reference.


This is all a question of scope. since huh isn't in the local scope of confused, python finds it in the global scope. Since it is found in the global scope, you can't assign to it unless you specifically say that you want to (using global as I have done above). However, if it's a list, once the list is found, you have access to all of that list's methods (including __setitem__, append, etc.)


As to the location of the error, this can be cleared up with a little disassembling:

>>> def confused(stuff):
...    print huh
... 

>>> import dis
>>> dis.dis(confused)
  2           0 LOAD_GLOBAL              0 (huh)
              3 PRINT_ITEM          
              4 PRINT_NEWLINE       
              5 LOAD_CONST               0 (None)
              8 RETURN_VALUE

>>> def confused2(stuff):
...    print huh
...    huh += 1
... 

>>> dis.dis(confused2)
  2           0 LOAD_FAST                1 (huh)
              3 PRINT_ITEM          
              4 PRINT_NEWLINE       

  3           5 LOAD_FAST                1 (huh)
              8 LOAD_CONST               1 (1)
             11 INPLACE_ADD         
             12 STORE_FAST               1 (huh)
             15 LOAD_CONST               0 (None)
             18 RETURN_VALUE   

You can see that in confused2, python is already trying to LOAD_FAST (meaning, look for a local variable) at the first line of the function. However, no local variable huh exists hence the Exception.

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • But I often change global dictionaries or lists within functions without declaring them as global... – mavix Jan 07 '13 at 18:09
  • @mattedgod -- If your learned from this, learn 1 more thing -- Using the `global` statement is rarely advised. – mgilson Jan 07 '13 at 18:09
  • @mgilson Well clearly I'm not using it much so I think I'm good on that front :) – Matt Dodge Jan 07 '13 at 18:10
  • @mavix -- See my update. Hopefully I've made the wording a little more clear... – mgilson Jan 07 '13 at 18:12
  • I see. Could you elaborate on why using `global` is ill advised? And any thoughts as to why it's complaining about the line with the `print` statement? – mavix Jan 07 '13 at 18:13
  • Bonus question: any idea why python makes me tell it explicitly that that's what I'm doing? – mavix Jan 07 '13 at 18:14
  • @mavix -- as to your bonus question, a fundamental tenant of python is "explicit is better than implicit" (`import this`). Global data is usually a bad idea as it makes your code harder to trace and debug. In general, having a function with side-effects (in this case incrementing `huh`) is surprising and violates the principle of encapsulation. – mgilson Jan 07 '13 at 18:18
  • 3
    @mavix In addition to what mgilson said, this rule for determining what's local and what's global removes the need for declaring local variables (by far the most common kind, so we don't want that to be verbose), while allowing access to read-only globals (which includes classes, functions, imports, etc. because those all share the same namespace) and still having a clear distinction between globals and locals just from looking at the function's code. –  Jan 07 '13 at 18:25
  • @delnan -- very nicely stated. – mgilson Jan 07 '13 at 18:34