From the Google Style Guide on lexical scoping:
A nested Python function can refer to variables defined in enclosing functions, but can not assign to them.
Both of these seem to check out at first:
# Reference def toplevel(): a = 5 def nested(): print(a + 2) nested() return a toplevel() 7 Out[]: 5 # Assignment def toplevel(): a = 5 def nested(): a = 7 # a is still 5, can't modify enclosing scope variable nested() return a toplevel() Out[]: 5
So why, then, does a combination of both reference and assignment in the nested function lead to an exception?
# Reference and assignment def toplevel(): a = 5 def nested(): print(a + 2) a = 7 nested() return a toplevel() # UnboundLocalError: local variable 'a' referenced before assignment
Enclosing (or nonlocal) scope is a special scope that only exists for nested functions. If the local scope is an inner or nested function, then the enclosing scope is the scope of the outer or enclosing function. This scope contains the names that you define in the enclosing function.
Enclosing Scope in Python Of course, a python variable scope that isn't global or local is nonlocal. This is also called the enclosing scope.
If we want to change the enclosing variable in an inner function, a nonlocal keyword is required. If we want to change a global variable in a function, a global keyword is required.
In first case, you are referring to a nonlocal
variable which is ok because there is no local variable called a
.
def toplevel(): a = 5 def nested(): print(a + 2) # theres no local variable a so it prints the nonlocal one nested() return a
In the second case, you create a local variable a
which is also fine (local a
will be different than the nonlocal one thats why the original a
wasn't changed).
def toplevel(): a = 5 def nested(): a = 7 # create a local variable called a which is different than the nonlocal one print(a) # prints 7 nested() print(a) # prints 5 return a
In the third case, you create a local variable but you have print(a+2)
before that and that is why the exception is raised. Because print(a+2)
will refer to the local variable a
which was created after that line.
def toplevel(): a = 5 def nested(): print(a + 2) # tries to print local variable a but its created after this line so exception is raised a = 7 nested() return a toplevel()
To achieve what you want, you need to use nonlocal a
inside your inner function:
def toplevel(): a = 5 def nested(): nonlocal a print(a + 2) a = 7 nested() return a
For anyone stumbling across this question, in addition to the accepted answer here, it is answered concisely in the Python docs:
This code:
>>> x = 10 >>> def bar(): ... print(x) >>> bar() 10
works, but this code:
>>> x = 10 >>> def foo(): ... print(x) ... x += 1
results in an
UnboundLocalError
.This is because 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. Consequently when the earlierprint(x)
attempts to print the uninitialized local variable and an error results.In the example above you can access the outer scope variable by declaring it
global
:>>> x = 10 >>> def foobar(): ... global x ... print(x) ... x += 1 >>> foobar() 10
You can do a similar thing in a nested scope using the
nonlocal
keyword:>>> def foo(): ... x = 10 ... def bar(): ... nonlocal x ... print(x) ... x += 1 ... bar() ... print(x) >>> foo() 10 11
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With