[Tutor] Sometimes unreferenced!?

Jeff Shannon jeff@ccvcorp.com
Thu Feb 6 12:24:08 2003


John Abbe wrote:

> def f():
>    records = [4, 5]
>    groupData += records


> UnboundLocalError: local variable 'groupData' referenced before 
> assignment


This is one of the standard local/global scope traps -- everyone trips 
over it at some point.  

The trick is that the parser examines every variable in a function as 
it's reading in the def, and decides at that time whether a variable is 
local or global.  Local variables get special treatment (resulting in 
much faster lookup times, etc), and the key to deciding whether a 
variable is local or global is whether the name is bound (or re-bound) 
within the function.  (Anything that creates a new object for the name 
to point to is re-binding.)

In your first function e(), you modify the object that groupData points 
to, by calling its reverse() method.  But it's still the same list 
object, so there's no re-binding, so groupData is considered a global 
name.  This means that Python looks it up properly at runtime.

In your f() function, though, you use += to create a *new* list (which 
is the union of groupData and records), and then re-bind groupData to 
point to that new list.  Here's where things get tricky -- Python sees 
the re-binding before running the code, so it presumes that groupData is 
a local variable here.  So a new, local name is created that shadows the 
global groupData.  Now, 'groupData += records' is (loosely) equivalent 
to 'groupData = groupData + records', so Python tries to look up the 
contents of the (local) groupData variable to add them to the contents 
of the 'records' variable -- but at this point, the local version of 
groupData doesn't point to anything yet.

There's a couple of ways to fix this problem.  The first is to simply 
avoid using global variables.  ;)  There's actually quite a few reasons 
to avoid them, as they tend to make confusing code easier to write.  If 
you re-write your functions so that groupData is passed in as a 
parameter and then returned as a result, it doesn't matter whether it 
shadows a global variable or not -- you'll always get the expected 
results because you're always dealing with local variables.

The second way is to simply inform the interpreter that the re-binding 
in f() is *not* intended to create a local variable.  This is done with 
the 'global' keyword -- you're letting Python know that, in this 
function, the given variable should be considered global and no 
shadowing local variable should be created.

def f():
    global groupData
    records = [4, 5]
    groupData += records

Hope this clarifies a few things.  Feel free to ask more questions if 
there's anything unclear, though.

Jeff Shannon
Technician/Programmer
Credit International