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
http://groups.google.com/group/comp.lang.python/browse_frm/thread/db9955da70c4e0ca
http://www.python.org/doc/2.4/ref/global.html
END.
#!/usr/bin/env python
ReplyDeleteglobal 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?
(Sorry about stupid blogger eating indentation by the way).
ReplyDeleteThe global statement would have to be *in* the function definition.
- Paddy.
Thank you for your reply. Following your advice, I've changed the program to the following:
ReplyDelete#!/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?
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:
ReplyDelete#!/usr/bin/env python
foo = 'foo'
def printFoo():
global foo
print foo
foo = 'bar'
printFoo()
printFoo()
foo = 'quux'
printFoo()
- Paddy.
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'.
ReplyDeleteThus, 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
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.
ReplyDeletethanks!
> you need to tell it to read the larger scope variable
ReplyDeleteNo, 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.