While most of our focus so far has been around where we can access namespaces, to truly get a full picture of scoping rules, we must also examine how Python handles scope resolution.
Scope resolution is a term used to describe a search procedure for a name in the various namespaces. A set of rules dictates the order that the search needs to follow.
In Python, the unofficial rule (often referred to in literature but does not exist in the official documentation) is known as the LEGB rule.
LEGB stands for Local, Enclosing, Global, and Built-in. These four letters represent the order of namespaces Python will check to see if a name exists. Here is a visualization of the order:
To see this rule in action, let’s take a look at two specific scenarios where Python is searching for a name. The first scenario is a nested function that wants to print a variable called
age = 27 def func(): def inner_func(): print(age) inner_func() func()
So what exactly happened here in terms of scope resolution? It went a bit like this:
First, Python looked in the local (The L of LEGB) scope that existed inside of
inner_func(). This is the lowest level of the LEGB rule and thus where Python starts the search for a name that is trying to be called (in this case via a
print()). Python then realized the name of
ageisn’t in the local namespace and continues the search to the upper levels of scope.
The second level Python examined is the enclosing scope (The E of LEGB) of
func(). Unfortunately, again the name of
agedoesn’t exist in the enclosing namespace, and Python moves upwards to higher scopes.
Next, Python arrives at the global scope and finds the name of
agein the global namespace. The search is finished, and the result is returned.
This process of scope resolution is crucial to understanding how programs are able to access names in different scopes. Keep in mind the order that Python searches always start at the lowest level (the local level) and always flows upward to the higher scopes.
The second scenario to examine is seeing what happens when we have two of the same name in different namespaces.
Let’s examine the same script but with a slight modification that creates a second name called
age in a different namespace. Here is what it looks like:
age = 27 def func(): age = 42 def inner_func(): print(age) inner_func() func()
Here the output will be
42 because Python could find a name (
age) in the enclosing scope and did not continue to search for the value up into the global scope. If Python cannot find a name in any of the four scopes it searches, it will return a
Using the LEGB rule, we are going to try and correct this function to behave how we expect it to. It should replace the color with a new provided color and print out the old and new colors. Try running the code to see what the first issue is.
The LEGB rule starts with “Local”. Let’s take a look at any local variable issues that we could be running into. Looking at each of the local variables, we can see that
to_update is local to the function
disp_color(), but we attempt to access it from
Move the initialization of to_update so that the scope is local to
change_color(). Try running the code now and see what happens.
Now we are not getting any errors, but the output is not correct. There doesn’t seem to be any encompassing scope issues, but there is an issue with the global variable
We are using the
global keyword to allow
color to be modified in order to print the new color. If we look at the order of operations, we modify the global variable before calling
To fix this, move the change to the variable
color to after
disp_color() is called but before the new color is output.
Run the code and see that happens!