Wednesday, July 12, 2006

Python Functions: Assignments And Scope

Explaining why this works:

n = [0]
def list_access():
n[0] = n[0] + 1
return n

try:
print "\nlist_access:", list_access()
except UnboundLocalError, inst:
print " ERROR:\n", inst

And this throws the exception:

m = 0
def int_access():
m = m + 1
return m

try:
print "\nint_access:", int_access()
except UnboundLocalError, inst:
print " ERROR:\n", inst

To execute a source program, the Python compiler compiles your
original source into 'byte codes' – a form of your program that
is easier for the Python interpreter to later run. In generating this
byte code, the byte code compiler will determine which variable names
in a function are local to that function, (so alowing it to optimise
accesses to such local names).

The rule for determining if a variable is local to a function is:

  • If there is a global statement for the name in the function
    then the name is accessed from the global scope.
  • If there is no global statement for the name, and if there
    are assignments to the 'bare' name within the function then the name
    is of local scope.
    ( A bare name assignment means assignment to a
    name, where the name occurs without attribute references,
    subscripts, or slicing s, just the bare name).
  • Otherwise the name will be looked up in reverse order of all
    enclosing scopes, until it is found.

In the second example, function int_access; name m is flagged as
local by the byte code compiler as the bare name is being assigned
to. The interpreter therefore looks for a value of m to increment
only in the local scope, cannot find a value, then raises the
UnboundLocalError exception.
In function list_access, the bare
name n is not assigned to, so n is found when looking back
through enclosing scopes.

References

  1. http://groups.google.com/group/comp.lang.python/browse_frm/thread/db9955da70c4e0ca

  2. http://pyref.infogami.com/assignments

  3. http://pyref.infogami.com/naming-and-binding

  4. http://www.python.org/doc/2.4/ref/global.html


END.