Navigate Python Global, Local and Nonlocal Variable

Photo by Timothy Dykes on Unsplash

If you are like me who have a few months experience of Python, then you might be in the same position as me — know something about Python, but not enough. So I spent sometime to gather bits and pieces of scattered knowledge to dig deeper into Python. In this blog, the piece will be the local, global and nonlocal variables.

Scenario A: declare a global and local variable and read separately

s = "This is global!"
def f():
s = "This is local!"
print(s)
f()
print(s)
=====================
This is local!
This is global!

Scenario B: declare a global and read it in local

s = "This is global!"
def f():
print(s)
f()
print(s)
=====================
This is global!
This is global!

Note here we declare a global variable “This is global!”and in the body of f() there is only “” trying to access the global variable, i.e., there is no local variable. So the print statement will just access the global variable as it is.

Scenario C: declare a global variable and modify it in local

s = "This is global!"
def f():
s = s + “This is local!”
print(s)
f()
print(s)
=====================
Traceback (most recent call last):
File "main.py", line 7, in <module>
f()
File "main.py", line 4, in f
s = s + "This is local!"
UnboundLocalError: local variable 's' referenced before assignment

The above is straightforward, as you try to modify a global variable directly before printing it out. Look at below though. Note that the error persists if you switch the order, compare it with above examples.

s = "This is global!"
def f():
print(s)
s = “This is local!”
f()
print(s)
=====================
Traceback (most recent call last):
File “main.py”, line 8, in <module>
f()
File “main.py”, line 4, in f
print(s)
UnboundLocalError: local variable ‘s’ referenced before assignment

What we are doing here is

  • first access global variable with a ,
  • and then assign a new value to

By assigning we are creating a local variable, as any variable modified or created inside a function is local, if it hasn’t been declared as a global variable.So we have s as both global and local variable in the same scope, and since Python thinks that we actually want a local variable due to the assignment to s, so the first print statement before the assignment raises an error.

Scenario D: declare a global variable in local through keyword

s = "This is global!"
def f():
global s
print(s)
s = "This is local!"
print(s)
f()
print(s)
=====================
This is global!
This is local!
This is local!

In above example, we use a global keyword for a variable which is inside a function so that it can be modified locally. So:

  • the first print statement still returns the unmodified global var in local scope,
  • the next print returns the modified version in the local scope,
  • the last one returns the modified version in the global scope.

Note that you can even declare a global variable in a local scope and only reference it in the outer scope:

def f():
global s
s = “This is local!”
f()
print(s)
===================
This is local!

Scenario E: declare a global variable in a nested function

def f():
s = "This is not global!"
def local():
global s
s = "This is global!"
print("Before calling local: " + s)
local()
print("After calling local: " + s)

f()
print(s)
==============================
Before calling g: This is not global!
After calling g: This is not global!
This is global!

As you can see, a variable defined in nested scope (also a local scope) of f will remain untouched. The first print statement reference the local variable . The next print statement still reference the same variable as the global variable s is declared but not accessed from the global scope yet, which is done in the last print statement.

So a variable defined inside of a function is local unless it is explicitly marked as global: .

In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a value anywhere within the function’s body, it’s assumed to be a local unless explicitly declared as global. — doc

Scenario F: declare a nonlocal variable

Let’s first have an example without all these magic keywords:

s = "This is global!"
def
f():
s = "This is nonlocal!"
def local():
s = "This is local!"
print(s)
local()
print(s)

f()
print(s)
==============================
This is local!
This is nonlocal!
This is global!

Now add the keyword. Nonlocal variables are used in nested functions , which means that the variable can be neither in the local nor the global scope. So now the 's is now also 's :

def f():
s = "This is not nonlocal!"
def local():
nonlocal s
s = "This is nonlocal!"
def local():
s = "This is local!"
print(s)
local()
print(s)

f()
print(s)
==============================
This is local!
This is nonlocal!
This is global!

However, note that the name of the variable listed in nonlocal statement must pre-exist, otherwise an error will raise (this is different to the global keyword).

Names listed in a statement, unlike those listed in a statement, must refer to pre-existing bindings in an enclosing scope (the scope in which a new binding should be created cannot be determined unambiguously).

Names listed in a statement must not collide with pre-existing bindings in the local scope.

doc

s = "This is global!"
def f():
nonlocal s
print(s)
f()
=====================
File "main.py", line 4
nonlocal s
^
SyntaxError: no binding for nonlocal 's' found

Scenario G: declare variables in a multi-level nested scope

So now it’s time to mix them together, what if we add another level of nested scope?

s = "This is global!"def outer_f():
s = "This is 1st level nested"
def inner_f():
s = "This is 2nd level nested!"
def local():
nonlocal s
s = "This is local!"
print(s)
local()
print(s)
inner_f()
print(s)
outer_f()
print(s)
===============================
This is local!
This is local!
This is 1st level nested
This is global!

See that only the closest declaration in is targeted with the keyword. This is because assignment of an object in python is recursively defined:

If the target is an identifier (name):

- If the name does not occur in a or statement in the current code block: the name is bound to the object in the current local namespace.

- Otherwise: the name is bound to the object in the global namespace or the outer namespace determined by , respectively.

- The name is rebound if it was already bound. This may cause the reference count for the object previously bound to the name to reach zero, causing the object to be deallocated and its destructor (if it has one) to be called.

Hope the above examples won’t confuse you further. Feel free to copy the code and play around in repls.

Happy Reading!