Variable Scope in Python

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:

  1. Local

  2. Enclosing

  3. Global

  4. Built-in

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:

The variable myVar has a local scope in funcA, which means that it can only be accessed inside the function.

If we try to access myVar outside funcA, we, expectedly, get an error because the variable is local to funcA:

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 funcA and 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 funcB inside funcA.

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 funcB:

As the local scope has no myVar variable, it will first check if there’s any enclosing scope.

Yes, there is — funcA!

Next, it will check if there’s myVar declared inside funcA.

Of course, there is!

Python refers to the value declared in the enclosing scope for myVar:

In the above code, if there was no myVar declared inside 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 funcA nor funcB have 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 print statements, we get the global value of 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 float, int, len, set, dict, range, str, 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?

Are you overwhelmed with the amount of information in ML/DS?

Every week, I publish no-fluff deep dives on topics that truly matter to your skills for ML/DS roles.

For instance:

Join below to unlock all full articles:

SPONSOR US

Get your product in front of 84,000 data scientists and other tech professionals.

Our newsletter puts your products and services directly in front of an audience that matters — thousands of leaders, senior data scientists, machine learning engineers, data analysts, etc., who have influence over significant tech decisions and big purchases.

To ensure your product reaches this influential audience, reserve your space here or reply to this email to ensure your product reaches this influential audience.

Reply

or to participate.