What is the scope of variables in Python?

Key takeaways:

  • The scope of a variable determines where that variable can be found and accessed in a program (in other words, the extent of the variable’s visibility or its lifetime).

  • Variables in Python can have different scopes, and understanding this concept is crucial for writing efficient, error-free code.

  • Local scope: Local variables are essential for temporary data handling within functions, helping to keep data isolated. By existing only during the function’s execution, they conserve memory and prevent accidental changes to data outside the function.

  • Global scope: Global variables are crucial for data that needs to be shared across multiple functions. They offer a single point of access throughout the program, making it easier to manage common data. However, using too many global variables can lead to unexpected bugs if modified without care.

  • Enclosed scope: Inner functions can access variables from the enclosing (outer) function. This is useful for creating factory functions or closures, where inner functions need context from their surrounding function to perform their tasks.

  • Nonlocal scope: The nonlocal keyword is powerful for modifying variables in the enclosing (non-global) scope, especially useful in nested functions. This lets inner functions update outer function variables without impacting global scope, preserving data integrity across the program.

  • Using global: The global keyword is vital when a function needs to modify a global variable directly, rather than creating a new local variable. While this can simplify code where global data must be changed, it should be used sparingly to avoid unintended side effects.

Types of scopes in Python

Python has four types of variable scopes, and each scope defines where variables can be accessed and modified:

  1. Local scope: Variables declared inside a function are part of the local scope and can only be accessed within that function.

  2. Enclosed (nonlocal) scope: This scope applies to variables in nested functions. Inner functions can access variables from their enclosing (outer) function.

  3. Global scope: Variables declared outside of all functions are part of the global scope and can be accessed throughout the entire program.

  4. Built-in scope: This includes Python’s built-in functions and keywords, such as print(), len(), break , try, and def. These are available everywhere in our code.

Now let’s look at each of these variable scopes in detail using a real-life example. We’ll use the example of a library management system to explain these scopes in a relatable and meaningful way. So let’s begin!

1. Python global scope

We start with the library’s basic setup: its name. This information is global to the entire system because it doesn’t change regardless of the operation being performed—whether it’s adding new books, registering members, or handling loans.

In this case, we create a variable called library_name that should be available to all parts of the program. Any other function that we write can access this information.

Global variables (like library_name here) are accessible everywhere in the code. They store data that needs to be shared across different functions.

library_name = "Library of Congress" # Global variable
def register_member():
print("Welcome to" + library_name + "!") # Accessing global variable
# Function call
register_member() # Prints the global variable

2. Python local scope

Now, let’s move to member registration. Each member’s details are unique to that specific function call. We don’t need these details outside the registration process, so we use local variables for this. Here, member_name exists only within the register_member() function. This isolates it from the rest of the program, making it a local variable.

Local variables (like member_name here) are accessible only within the function that creates them. They help in avoiding conflicts with similarly named variables elsewhere in the code.

def register_member():
member_name = "Iqbal" # Local variable since its defined inside the function
print("New member registered: " + member_name) # Accessing local variable
register_member()

Accessing the local variable from within and outside of the function

That’s simple. But can this local variable be accessed outside of the function? Let’s see.

def register_member():
member_name = "Iqbal" # Local variable since its defined inside the function
print("New member registered: " + member_name) # Accessing local variable
register_member()
print("The registered member is: " + member_name) # Accessing local variable outside of the function

The above code gives a NameError when we try to access the local variable member_name. This is because no such variable exists outside of the register_member() function’s scope.

Now, what if we had a local and global variable of the same name?

member_name = "Joseph"
def register_member():
member_name = "Iqbal" # Local variable since its defined inside the function
print("New member registered: " + member_name) # Accessing local variable
register_member()
print("The registered member is: " + member_name) # Accessing local variable outside of the function

When a global variable shares the same name as a local variable, Python treats them as separate entities. Here, the local member_name inside the function is independent of the global member_name.


Using global keyword for global variables

There are cases when we might want to access and modify the global variables inside a function. Let’s say there’s a global variable borrowing limit for borrowing books. To update the library’s borrowing list, we have a function called update_borrowing_limit().

To update this policy, we need to update the global variable by reassigning it inside the function update_borrowing_limit. Look at the code below and execute it.

# Global borrowing policy
borrowing_limit = 5 # Users can borrow up to 5 books
def update_borrowing_limit(new_limit):
borrowing_limit = new_limit # Reassign to a new borrowing limit
print("Current borrowing limit:", borrowing_limit)
update_borrowing_limit(7)
print("Updated borrowing limit:", borrowing_limit)

The updated borrowing limit is not updated at all. This is because we cannot update/reassign a global variable inside a function unless we use the global keyword. Without the global keyword, Python would create a local variable inside the function and leave the global borrowing_limit unchanged.

We need to explicitly write the global keyword with the global variable that we want to update.

The global keyword allows functions to modify global variables. This is essential when we want changes to apply across all parts of the program.

See the line 5 in the code below and run the code:

# Global borrowing policy
borrowing_limit = 5 # Users can borrow up to 5 books
def update_borrowing_limit(new_limit):
global borrowing_limit # Use 'global' to modify the global variable
borrowing_limit = new_limit # Reassign to a new borrowing limit
print("Current borrowing limit:", borrowing_limit)
update_borrowing_limit(7)
print("Updated borrowing limit:", borrowing_limit)

This time the borrowing limit is updated to 7.

Note: The global keyword is not needed for printing and accessing, just for modifying. Feel free to change the variable names, scopes, or values to see how the change affects program behavior.


3. Enclosed scope

Imagine that we want to add a feature where members can check access to premium sections of the library. To implement this, we need two levels of functions: one for general access and another for specific section access. The access code for each section will be set in the outer function, while the inner function checks it. This brings us to the enclosed scope.

Here’s how we define access to special library sections. The inner function will check the access code, which is defined in the outer function. The access_code variable belongs to the enclosed scope because it’s defined in the outer library_section() function but is used inside the inner check_access() function.

Enclosed scope allows the inner function to access variables from its enclosing function. This is useful when an inner function needs to remember data from the outer function.

def library_section():
access_code = "SECRET123" # Enclosed scope variable
def check_access():
print("Access code for this section: " + access_code) # Accessing enclosed variable
check_access()
library_section()

4. Nonlocal scope

Now, suppose we want to allow premium members to enjoy an updated discount rate. The discount rate is set when membership is defined but can change. The inner function can modify this discount rate, but only if it’s allowed to access the variable in the enclosing scope using the nonlocal keyword.

We use the nonlocal scope to modify variables in the enclosing function without making them global. The nonlocal keyword lets the inner function update_discount() modify the discount_rate in the outer membership() function. Without nonlocal, the change wouldn’t apply to the variable in the outer function.

Nonlocal scope allows modifying variables in the enclosing (non-global) scope. It is useful when an inner function needs to update information from the outer function, like changing a membership discount in our example.

def membership():
discount_rate = 5 # Initial discount rate
def update_discount():
nonlocal discount_rate # Modify enclosed variable
discount_rate = 10 # Updated discount rate
update_discount()
print("Updated discount rate: ", discount_rate)
membership()

Quiz yourself

Now solve the quiz on Python variable scopes, designed to not only test knowledge but also teach you the subtleties of variable scopes:

1

What do we get when executing the code below?

x = 10

def test():
    x = x + 5
    print(x)

test()

A)

15

B)

TypeError

C)

UnboundLocalError

D)

None of the above

Question 1 of 50 attempted

Exercise: Correct the code

The code below is intended to count the number of times a function is called, but it has scope-related errors. Fix the code so that it correctly counts and prints the number of function calls.

The code is supposed to keep track of:

  1. A global count of the total number of times any counter() function is called across all instances.

  2. A local count that resets each time the outer() function is called and only counts calls to counter() within each specific call to outer().

Your task is to correct these mistakes so that both total_count and local_count work as intended.

total_count = 0 # This should keep track of the total number of times `counter` is called
def outer():
local_count = 0 # This should reset each time `outer` is called
def counter():
total_count += 1 # This should increment the global total count
local_count += 1 # This should increment the local count for this call of `outer`
print("Local count in this call to outer:", local_count)
counter()
counter()
outer()
print("Total count after first call to outer:", total_count)
outer()
print("Total count after second call to outer:", total_count)

The LEGB rule

Now let’s look at the rule (LEGB) that Python uses to search for variables in different scopes. To help us remember, we’ve broken down the acronym LEGB to understand what it entails:

  1. L (Local): Python looks in the current local scope first (inside the function).

  2. E (Enclosed): If the variable is not found locally, Python checks for it in any enclosing functions (if there are any).

  3. G (Global): If the variable is not found in an enclosing function, Python checks the global scope (variables defined at the top level of the script or module).

  4. B (Built-in): If Python still hasn’t found the variable, it checks the built-in scope, which contains Python’s built-in functions and constants like print(), len(), etc.

Each scope defines where variables can be accessed and modified, making it crucial to understand how they work when writing Python programs.

Become a Python developer with our comprehensive learning path!

Ready to kickstart your career as a Python Developer? Our Become a Python Developer path is designed to take you from your first line of code to landing your first job.

This comprehensive journey offers essential knowledge, hands-on practice, interview preparation, and a mock interview, ensuring you gain practical, real-world coding skills. With our AI mentor by your side, you’ll overcome challenges with personalized support, building the confidence needed to excel in the tech industry.

Frequently asked questions

Haven’t found what you were looking for? Contact Us


What is the LEGB rule in Python?

When we use a variable name, how does Python know where to find its value? It uses the LEGB rule. LEGB stands for Local, Enclosing, Global, Built-in. These are the possible scopes of a variable in Python. We also use these words to describe namespaces, which store variable names and values and are how Python determines scope.

The order Local, Enclosing, Global, Built-in is the order of precedence for scope resolution. That means Python searches for the value of a name in each of these namespaces in turn.


What is scope?

The scope of a variable is the region of space where the value of that variable is valid.

The main reason for a scope is to keep variables with the same names confined to separate parts of a program. Programmers may use the same variable names repeatedly; therefore, the scope defines the region in which that variable is valid.


How does the global keyword work in Python?

The global keyword is used inside a function when you need to modify a global variable. Without it, Python will assume you’re creating a new local variable, and the global variable will remain unchanged.


What is the difference between global and nonlocal in Python?

The global keyword allows you to modify global variables inside a function.

The nonlocal keyword is used inside nested functions to modify variables from the enclosing (non-global) function, but it doesn’t affect global variables.


Free Resources