The nonlocal Declaration
00:00
What if you need to modify a value in an enclosing function? The global
declaration won’t work, because the variable is not in the global namespace. In this case, you can use the nonlocal
declaration.
00:16
In this example, the enclosed function g()
wants to change the variable x
that was defined in the enclosing function f()
. When you run the function f()
, then you will see that x
still has the value 20
when it is printed after the function g()
was called.
00:38
That is because a new name x
with the value 40
is added to the local namespace of the enclosed function g()
. Using a global
declaration won’t work in this case, because x
is not in the global namespace. As you can see, the value of x
is still 20
when it was printed after the function g()
was called. In fact, the global x
statement not only fails to provide access to x
in the enclosing scope, it also creates an object called x
in the global scope whose value is 40
.
01:26
If you want to modify the value of x
from inside the enclosed function g()
, you need to use the nonlocal
keyword instead of the global
keyword. Names specified after the nonlocal
keyword refer to variables in the nearest enclosing scope, which is, in this case, the variable x
in the enclosing function f()
. As you can see, after running the function f()
, the value of x
changed to 40
from within the enclosed function g()
.
02:04
The global
and nonlocal
keywords give developers a lot of flexibility, but that flexibility also comes with some caveats we’ll have a look at in the final lesson of this section.
Bartosz Zaczyński RP Team on March 8, 2021
@SatyaRavipati TL;DR You can’t unambiguously access the variable x
in the outermost scope.
Variables have a scope, which defines their visibility and lifetime. Scopes are structured in a hierarchy that the Python interpreter can scan through when searching for a given symbol. It always starts looking for variables in the local scope. If no variable with the expected name is found in the local scope, then it moves on to the nearest enclosing scope and continues until the global scope. Finally, Python will try to look for the variable among the built-in symbols.
That is known as the LEGB rule, which is short for Local, Enclosing, Global, and Built-in.
Normally, when you declare a local variable with the same name as a global one or a variable defined in the enclosing scope, it’s known as masking or shadowing:
x = 42 # Global variable
def function():
x = 555 # Local variable shadows the global one
print(x)
But, what if you wanted to modify the global variable x
, or “rebind” it, instead of creating a new local variable with the same name in the function? You can take advantage of the global
keyword in such a case:
x = 42 # Global variable
def function():
global x
x = 555 # Global variable with a new value
function()
print(x)
This will instruct Python to look for an existing variable in the global scope straight away. Interestingly, you don’t need to declare a variable like that if you only want read access to the global variable:
x = 42 # Global variable
def function():
print(x) # Reads the global variable
(Note: You can modify the value of a global variable without using the global
keyword if it’s a mutable data type, but you won’t be able to rebind the variable to another object.)
Things get more interesting when you add intermediate scopes to the mix. Using the nonlocal
keyword lets you assign new values or “rebind” existing variables that are neither local nor global but somewhere in the middle:
x = 42 # Global variable
def outer():
x = 555 # Local to outer()
def medium():
x = 777 # Local to medium()
def inner():
nonlocal x
x = 999 # Rebind x local to medium()
Unfortunately, it won’t tell you upfront which of the enclosing scopes will be modified as Python will look for the variable outwards and stop at the first occurrence.
Become a Member to join the conversation.
SatyaRavipati on March 6, 2021
Hi Johan, this great tutorial and content is very detailed and valuable. I have a quick question from below code snippet, can you help me with it.
When I want to acess value of x (value 10 in function e) inside g(), is it possible to get this using namespace?