[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