1

Firstly, it's related to this function:

a = 3
def b():
    a = a+1
    return a 

returns an error.

Isn't it when the local environment of b couldn't find 'a', then it goes to the parent environment where b is defined, that is, where 'a' is found to be 3?

Why must I reference it with a global a in order for it to work? Meaning, do I have to do this only when the immediate parent environment is the global environment? Because the below function seems to be able to reference the parent environment:

def b():
    def c():
        print c
    return c()
Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
kwotsin
  • 2,882
  • 9
  • 35
  • 62
  • You might be interested in reading [this](http://stackoverflow.com/questions/9264763/unboundlocalerror-in-python) – Pukki Jan 16 '16 at 07:31
  • If you just read its value, then it will look upward to the nearest enclosing scope that contains it. But if you try to modify its value, then it will create a local variable unless you declare it `global`. In Python 3, you can declare variables `nonlocal` as well. – Tom Karzes Jan 16 '16 at 07:42

3 Answers3

4

This is such a common issue that is is actually in the Python FAQ:

[...] when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope. Since the last statement in foo assigns a new value to x, the compiler recognizes it as a local variable.

So, Python sees you're assigning to a local variable with a = .. and thinks, hey, a local variable, I'm fine with this. But, when it actually bumps into the content of that assignment (a + 1) and sees the same name a it complains because you're trying to reference it before assigning it.

This is why the following doesn't raise an error:

def b():
    a = 20
    a = a + 2
    return a

You first make an assignment, a is treated as local to the function b and when a = a + 2 is encountered, Python knows a is a local variable, no confusion is present here.


In the other case you added:

def b():
    def c():
        print c
    return c()

You are only referencing a name enclosed in the scope of function b, referencing. If you changed this to a similar assignment as before, you'll get the same UnboundLocalError:

def b():
    def c():
        c = c + 20
    return c()
Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
3

Because you are doing an assignment and the scope has to be clarified.

Your case:

a = 3
def b():
    a = a + 1
    return a

print(b())

Output:

Traceback (most recent call last):
  File "./tests/test1.py", line 12, in <module>
    print(b())
  File "./tests/test1.py", line 9, in b
    a = a + 1
UnboundLocalError: local variable 'a' referenced before assignment

When you do the assignement a = ... you have created always a local variable unless it has been declared global which is not the case. Local resolution takes precedence.

Your example would obviously work with:

a = 3
def b():
    global a
    a = a + 1
    return a

print(b())

Which outputs:

4

But using globals, imho, should not be the way and hence passing a as a parameter to function b would be it:

a = 3


def b(a):
    a = a + 1
    return a

print(b(a))

Which agains produces the expected 4.

EDIT:

Following the edition in the OP: you can always "reference" the global scope (and parent scope). But if you make assignments they take place in the local scope.

If your code (which is wronly indented and contains a new c which isnt' anywhere) is meant to do this: a = 3

def b():
    def c():
        print(a)
    return c()

b()

It works ... and prints 3 because there is NO assignment and only referencing. As soon as you executing an assignment a would again be created in the local scope.

mementum
  • 3,153
  • 13
  • 20
1

Assignment will create a new variable in Python.
Here may help you.

Ren
  • 2,852
  • 2
  • 23
  • 45