Mainly Tech projects on Python and Electronic Design Automation.

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.

7 comments:

  1. #!/usr/bin/env python

    global foo
    foo = 'foo'

    def printFoo():
    print foo
    foo = 'bar'

    printFoo()

    ^-- why does this not work then? I've told the compiler that foo is a global, so why does it choke?

    ReplyDelete
  2. (Sorry about stupid blogger eating indentation by the way).

    The global statement would have to be *in* the function definition.

    - Paddy.

    ReplyDelete
  3. Thank you for your reply. Following your advice, I've changed the program to the following:

    #!/usr/bin/env python

    global foo
    foo = 'foo'

    def printFoo():
    print foo
    global foo
    foo = 'bar'

    printFoo()
    printFoo()
    foo = 'quux'
    printFoo()

    (you can guess where the tabs should go).
    Python thinks it a syntax error if you say for instance
    global foo='bar' # I'm trying to tell it to use the global foo.

    When you run this program you get the following output:
    $ python foo.py
    foo.py:6: SyntaxWarning: name 'foo' is used prior to global declaration
    def printFoo():
    foo
    bar
    quux

    i.e. it does what we want, but gives me a warning. What am I still missing? Is there a 'right' way to do what I'm trying to do?

    ReplyDelete
  4. Try the following, I've removed the first global and moved the second global statement so it comes before any use of the global variable in the function:

    #!/usr/bin/env python
    foo = 'foo'

    def printFoo():
      global foo
      print foo
      foo = 'bar'

    printFoo()
    printFoo()
    foo = 'quux'
    printFoo()


    - Paddy.

    ReplyDelete
  5. Brill, I think I've got it now. Essentially 'global x' means 'Within this current scope, when I say x I mean the module's x'.

    Thus, as you say, you don't need the global round the initial declaration (where foo='foo'), but you do need it within the function to say 'use the global foo'.

    :D

    ReplyDelete
  6. Maxwell, thanks a lot for your description, i finally understood that global doesn't go with the declaration, it's where you are reading the variable that you need to tell it to read the larger scope variable. just a little backwards from what i'm use to.

    thanks!

    ReplyDelete
  7. > you need to tell it to read the larger scope variable

    No, not 'read'. Reading works anyway. If you intend to _rebind_ the name ("modify" the variable), then you need to explicitly say it will modify the global scope.

    ReplyDelete

Followers

Subscribe Now: google

Add to Google Reader or Homepage

Go deh too!

whos.amung.us

Blog Archive