A common acronym to be familiar with is LEGB:
- Local
- Enclosed
- Global
- Built-in
This is the order in which Python will search the namespaces to find variable assignments.
Local
The local namespace is everything that happens within the current code block. Function definitions contain local variables that are the first thing that is found when Python looks for a variable reference. Here, Python will look in the local scope of foo
first, find x
with the assignment of 2
and print that. All of this happens despite x
also being defined in the global namespace.
x = 1
def foo():
x = 2
print(x)
foo()
# prints:
2
When Python compiles a function, it decides whether each of the variables within the definition code block are local or global variables. Why is this important? Let's take a look at the same definition of foo
, but flip the two lines inside of it. The result can be surprising
x = 1
def foo():
print(x)
x = 2
foo()
# raises:
UnboundLocalError: local variable 'x' referenced before assignment
This error occurs because Python compiles x
as a local variable within foo
due to the assignment of x = 2
.
What you need to remember is that local variables can only access what is inside of their own scope.
Enclosed
When defining a multi-layered function, variables that are not compiled as local will search for their values in the next highest namespace. Here is a simple example.
x = 0
def outer_0():
x = 1
def outer_1():
def inner():
print(x)
inner()
outer_1()
outer_0()
# print:
1
When inner()
is compiled, Python sets x
as a global variable, meaning it will try to access other assignments of x
outside of the local scope. The order in which Python searches for a value of x in moving upward through the enclosing namespaces. x
is not contained in the namespace of outer_1
, so it checks outer_0
, finds a values and uses that assignment for the x
within inner
.
x --> inner --> outer_1 --> outer_0 [ --> global, not reached in this example]
You can force a variable to not be local using the keywords nonlocal
and global
(note: nonlocal
is only available in Python 3). These are directives to the compiler about the variable scope.
nonlocal
Using the nonlocal
keyword tells python to assign the variable to first instance found as it moves upward through the namespaces. Any changes made to the variable will be made in the variable's original namespace as well. In the example below, when 2
is assigned x
, it is setting the value of x
in the scope of outer_0
as well.
x = 0
def outer_0():
x = 1
def outer_1():
def inner():
nonlocal x
print('inner :', x)
x = 2
inner()
outer_1()
print('outer_0:', x)
outer_0()
# prints:
inner : 1
outer_0: 2
Global
The global namespace is the highest level namespace that you program is running in. It is also the highest enclosing namespace for all function definitions. In general it is not good practice to pass values in and out of variables in the global namespace as unexpected side effects can occur.
global
Using the global
keyword is similar to non-local, but instead of moving upward through the namespace layers, it only searches in the global namespace for the variable reference. Using the same example from above, but in this case declaring global x
tells Python to use the assignment of x
in the global namespace. Here the global namespace has x = 0
:
x = 0
def outer_0():
x = 1
def outer_1():
def inner():
global x
print('inner :', x)
inner()
outer_1()
outer_0()
# prints:
0
Similarly, if a variable is not yet defined in the global namespace, it will raise an error.
def foo():
z = 1
def bar():
global z
print(z)
bar()
foo()
# raises:
NameError: name 'z' is not defined
Built-in
Last of all, Python will check for built-in keywords. Native Python functions such as list
and int
are the final reference Python checks for AFTER checking for variables. You can overload native Python functions (but please don't do this, it is a bad idea).
Here is an example of something you SHOULD NOT DO. In dumb
we overload the the native Python list
function by assigning it to 0
in the scope of dumb
. In the even_dumber
, when we try to split the string into a list of letters using list
, Python will find the reference to list
in the enclosing namespace of dumb
and try to use that, raising an error.
def dumb():
list = 0
def even_dumber():
x = list('abc')
print(x)
even_dumber()
dumb()
# raises:
TypeError: 'int' object is not callable
You can get back the original behavior by referencing the global definition of list
using:
def dumb():
list = [1]
def even_dumber():
global list
x = list('abc')
print(x)
even_dumber()
dumb()
# returns:
['a', 'b', 'c']
But again, DO NOT DO THIS, it is bad coding practice.
I hope this helps bring to light some of how the namespaces work in Python. If you want more information, chapter 7 of Fluent Python by Luciano Ramalho has a wonderful in-depth walkthrough of namespaces and closures in Python.