views:

46

answers:

2

So I'm having a very strange error right now. I found where it happens, and here's the simplest code that can reproduce it.

def cause_an_error():
    some_var = False
    def some_nested_func():
        print some_var
        #some_var = True
    some_nested_func()

>>> cause_an_error()
False

If you run it as-is, it prints "False". But if you uncomment that line, it gives an error:

    Traceback (most recent call last):
  File "<pyshell#224>", line 1, in <module>
    cause_an_error()
  File "<pyshell#223>", line 6, in cause_an_error
    some_nested_func()
  File "<pyshell#223>", line 4, in some_nested_func
    print some_var
UnboundLocalError: local variable 'some_var' referenced before assignment

Notice the error occurs on the line that worked just fine before.

Any ideas what causes this and how I can fix this? I'm using Python 2.6.1.

+1  A: 

The inner function, some_nest_func, has its own scope, and the fact that you are assigning to some_var somewhere within that scope makes some_var local to that scope. This has the effect of hiding the some_var assigned in the outer function. They are two different variables.

The upshot of all this is that you are printing some_nest_func's version of some_var before you assign to it.

If you need to manipulate some variable both in and outside the inner function, there isn't a clean way to it, AFAIK. A simple work-around is to replace the variable with a single-element array and manipulate that isn't (don't assign to the array variable, of course).

Marcelo Cantos
That makes sense to me. But shouldn't the non-local variable only be shadowed *after* the assignment?
Wallacoloo
The shadowing happens at compile time. If Python finds an assignment anywhere within the scope, all references become local.
Marcelo Cantos
Hrmm. Then how do I work around this?
Wallacoloo
I've amended my answer. Also see Ignacio's answer for Python 3.0.
Marcelo Cantos
+1  A: 

This can't be fixed in 2.x without refactoring the code. 3.x adds nonlocal to solve this.

Ignacio Vazquez-Abrams