Variable Scope: A Fundamental Programming Concept That No Python Programmer Must Ignore
A simple and intuitive guide to Python scope and LEGB rule.
In yesterday’s post, we discussed a fundamental difference between for-loops and list comprehension in Python.
Here’s the visual from that post for a quick recap:
In a gist:
After running a for-loop, the loop variable is still accessible.
After running a list comprehension, the loop variable is NOT accessible.
As I mentioned yesterday, this is closely linked to the idea of scope in Python, so let’s understand it today.
What is variable scope and why is it needed?
Simply put, variable scope refers to regions of our program in which a particular variable can be accessed.
The above idea is not specific to Python. Instead, it is valid for programming in general.
The objective is to limit variable access to only specific areas of our code so that we can prevent modifying variables from any random part of the program.
Thus, when we access ANY variable, it is scope that entirely determines how the names will be looked up in our code.
In Python, specifically, scope resolution has four levels.
Whenever we access any name in Python — a variable name, function name, class name, etc., the Python interpreter searches it in the following order:
This is often called the LEGB rule.
LEGB scope resolution in Python
Let’s understand them one by one!
#1) Local scope
The simplest example of local scope would be variables declared inside a function, as depicted below:
myVar has a local scope in
funcA, which means that it can only be accessed inside the function.
If we try to access
funcA, we, expectedly, get an error because the variable is local to
Moving on, if we have two non-enclosed functions, we notice that the variables declared inside one function are not accessible inside the second function:
This is the idea of local scope, and in Python, it gets the highest priority during scope resolution.
In other words, if the same variable name exists at multiple places in a program, Python will resort to the specific variable that was in the local scope from where it was accessed.
For instance, in the code below,
myVar is declared in both
funcB. When we invoke
funcA(), Python finds
myVar in the local scope and prints it:
If the variable is found in the local scope, great.
If not, it moves to the enclosing scope (if any) to find the variable.
Before we move to enclosing scope…
One thing to always remember about local scope is that it is created at every function call, i.e., it is not created when the function is defined.
Thus, one can have as many distinct local scopes as the number of times they invoke a function, including recursive calls.
#2) Enclosing scope
In Python, the enclosing scope is defined when we have nested functions — one or more functions inside another function.
For instance, consider the latest code above. Let’s declare
The local and enclosing scope with respect to
funcB is shown below:
Now consider the following code, where
myVar is not available in the local copy of
As the local scope has no
myVar variable, it will first check if there’s any enclosing scope.
Yes, there is —
Next, it will check if there’s
myVar declared inside
Of course, there is!
Python refers to the value declared in the enclosing scope for
In the above code, if there was no
funcA, Python would have moved to the Global scope, which comes next in the priority order.
#3) Global scope
Global scope, as you may have already guessed, is the broadest scope.
Here, variables and functions are declared outside any specific block, function, or class.
This makes them accessible (not directly modifiable though) from any part of the program, regardless of where they are defined.
For instance, in the code below, neither
myVar variable defined in their respective local scope:
There is no enclosing scope either.
Thus, Python finds if
myVar is available in the global scope, and it is indeed available.
Thus, during the
myVar — 10.
As an exercise, can you tell the output in this case:
#4) Built-in scope
The last in the priority order of scope resolution is built-in.
These are variable names that are automatically loaded in the global Python scope when we:
either run a script,
or open an interactive Python session.
These built-in variables can be accessed using the
__builtins__ name, and there are
>150 of them:
Some common examples include
zip, and more.
If a variable is not found in the local scope, not in the enclosing scope, and not even in the global scope, Python tries to find it in the built-in scope.
And that is how scope resolution works in Python.
Now, let’s go back to the list comprehension vs. for loop example.
The loop variable (
loop_var) is not accessible after running list comprehension because it is defined in its local scope.
However, the loop variable is defined in global scope in for-loop in the above code. Thus, it is still accessible after running the loop.
Hope you learned something new today.
👉 Over to you: Can you tell how we can modify a global variable in the local scope of a function?
Thanks for reading Daily Dose of Data Science! Subscribe for free to learn something new and insightful about Python and Data Science every day. Also, get a Free Data Science PDF (550+ pages) with 320+ tips.
👉 If you liked this post, don’t forget to leave a like ❤️. It helps more people discover this newsletter on Substack and tells me that you appreciate reading these daily insights.
The button is located towards the bottom of this email.
Thanks for reading!
Latest full articles
If you’re not a full subscriber, here’s what you missed last month:
To receive all full articles and support the Daily Dose of Data Science, consider subscribing:
👉 Tell the world what makes this newsletter special for you by leaving a review here :)
👉 If you love reading this newsletter, feel free to share it with friends!