views:

107

answers:

3

This wants me to dig deeper in Python sources, but since there are many people on SO that already done that, I would love to hear their pointers.

>>> import os
>>> def scope():
...     print os
...     import os
... 
>>> scope()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in scope
UnboundLocalError: local variable 'os' referenced before assignment

It seems to me that when parser interprets the file, it automatically creates local scope for scope function, that makes os "detached" from global scope.

Is it true? Anyone care to point me where I can find more about scope implementation?

EDIT: Also, this is not special case of imports, this works for usual variables too.

+4  A: 

When you call scope() Python sees that you have a local variable called os used inside your method (from the import inside scope) so this masks the global os. However when you say print os you haven't reached the line and executed the local import yet so you see the error regarding reference before assignment. Here are a couple of other examples that might help:

>>> x = 3
>>> def printx():
...     print x # will print the global x
...
>>> def printx2():
...     print x # will try to print the local x
...     x = 4
...
>>> printx()
3
>>> printx2()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in printx2
UnboundLocalError: local variable 'x' referenced before assignment

And going back to your os example. Any assignment to os has the same effect:

>>> os
<module 'os' from 'C:\CDL_INSTALL\install\Python26\lib\os.pyc'>
>>> def bad_os():
...     print os
...     os = "assigning a string to local os"
...
>>> bad_os()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in bad_os
UnboundLocalError: local variable 'os' referenced before assignment

Finally, compare these 2 examples:

>>> def example1():
...     print never_used # will be interpreted as a global
...
>>> def example2():
...     print used_later # will be interpreted as the local assigned later
...     used_later = 42
...
>>> example1()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in example1
NameError: global name 'never_used' is not defined
>>> example2()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in example2
UnboundLocalError: local variable 'used_later' referenced before assignment
mikej
Thanks - but are there any references about internal implementation? I'd like to see how to parser actually works. Is it so that Python creates dictionary/struct for every scope and only looks for the outer scope if there is nothing in inner one? Just want to clear my thoughts.
Almad
this is not a parser issue, it's a runtime "find closest scope with a name matching x" thing.
Warren P
A: 

Any binding of a variable inside a function makes the variable local to that function. import, def and class are all equivalent to assignment in this respect.

So yes, when the compiler compiles your file it creates a local variable os which is scoped separately from the global os.

See the Python tutorial for more information. In particular http://docs.python.org/tutorial/classes.html section 9.2

Duncan
A fuller reference is http://docs.python.org/reference/executionmodel.html
Mark
+1  A: 

Lexical scoping is a common thing and most well designed languages, whether interpreted or compiled use it.

I had not tried this in a while, but note the nifty "global" keyword, and its use below:

o = 1
def foo():
    global o
    o = 2
foo()
print o

Without the "global" line, the modification to o is localized, and "print o" prints 1. With the "global o" line included, it prints 2. We call this way that my function (without the global o) above would have its own variables. The global thing above is a way to specifically request an exception to the normal lexical scoping.

True lexical scoping is something that Python 1.0 lacked, and which Python has had for a long time (since at least 1.6, if I remember correctly). There were only two scopes, local and global, and any intermediate scopes were inaccessible.

Warren P