Python Scope and Namespaces Explained: LEGB Rule, global & nonlocal

Python Scope and Namespaces Explained: LEGB Rule, global & nonlocal — cover image

Python Scope and Namespaces: LEGB, global, and nonlocal

Key Takeaways

  • By mapping names to objects, a Python namespace enables Python to manage variables, functions, classes, and modules without encountering naming conflicts.
  • Scope determines where a name can be accessed in your program, while a namespace determines where that name is stored.
  • Every function call creates a new local namespace, ensuring that local variables remain isolated from the rest of the program.
  • Python resolves names using the LEGB rule, searching the Local, Enclosing, Global, and Built-in scopes in that order.
  • You can create predictable code and steer clear of issues like NameError and UnboundLocalError by having a solid understanding of namespaces and scope.

Introduction

Have you ever defined a variable in one part of your program, only to see Python raise a NameError or use a different value than you expected? In most cases, the problem isn't the variable itself, it's where Python is looking for that name.

Whether it's a variable, function, or class, every identifier you construct has a distinct namespace and is only available inside a specified scope. These two ideas dictate how Python locates names, avoids conflicts, and makes sure that various software components don't inadvertently overwrite one another's data.

Understanding what is Python namespace and what is scope in Python is essential for writing reliable code. It also makes concepts like the LEGB rule, the global keyword, and the nonlocal keyword much easier to understand.

In this guide, you'll learn how Python stores names, how it searches for them during execution, and how namespaces and scopes work together to resolve every name your program uses.

Understanding Name Binding in Python

Before learning about namespaces, it's important to understand name binding.

When you write an assignment statement, Python does not store a value inside a variable. Instead, it binds a name to an object.

For example:

x = 10

This statement creates the integer object 10 and binds the name x to it.

Later, if you write:

x = 20

The original object is not changed by Python. Rather, it associates a different integer object (20) with the name x. If nothing refers to the preceding object, it stays unaltered and could eventually be deleted by Python's trash collector.Namespaces also solve an important problem: name collisions.

This behavior explains why Python variables are better thought of as references to objects rather than containers that hold values.

Name binding occurs in several situations, including:

  • Assigning a value to a variable
  • Defining a function with def
  • Creating a class with class
  • Importing modules
  • Using loop variables or exception variables

Each time one of these operations occurs, Python creates or updates a binding between a name and an object.

Understanding name binding provides the foundation for learning Python namespaces, because namespaces simply organize these bindings.

What Is a Namespace in Python?

A namespace is a collection that maps names to objects. Whenever you create a variable, function, class, or module, Python records that relationship inside a namespace.

In simple terms, if name binding creates the relationship, the namespace stores it.

For example:

name = "Alice"
age = 22

The namespace stores bindings similar to:

Name Object
name "Alice"
age 22

If you define namespace in one sentence, it is:

A namespace is a mapping between names and the objects they reference.

This namespace meaning is central to Python's execution model. Instead of searching your entire program for a variable, Python looks inside the appropriate namespace to find the object associated with that name.

Another significant issue that namespaces address is name collisions.

Assume that a variable called count is used by two distinct functions. One variable could overrun another without distinct namespaces, leading to inaccurate outcomes. Python permits identical names to exist independently and without conflict by storing names in distinct namespaces.

Understanding what is Python namespace makes it easier to understand why variables with the same name can behave differently depending on where they are defined.

Types of Namespaces in Python

Python creates different namespaces during program execution. Each serves a specific purpose and helps organize names according to where they are defined.

Built-in Namespace

Names that Python automatically offers are included in the built-in namespace. Every Python application has access to these names without the need for an import.

Examples include:

  • print()
  • len()
  • sum()
  • type()
  • range()

Because these functions belong to the built-in namespace, you can call them directly.

Global Namespace

The global namespace belongs to a module or Python file. Names defined outside all functions and classes are stored here.

Example:

language = "Python"

def show_language():
    print(language)

Here, language belongs to the global namespace and can be accessed throughout the module unless a local variable with the same name exists.

Local Namespace

A local namespace is created whenever a function is called.

def greet():
    message = "Hello"
    print(message)

Here, message exists only inside the greet() function.

An important point is that every function call creates a new local namespace. Even when the same function is called multiple times, Python creates a fresh local namespace for each execution, ensuring that local variables from one call don't interfere with another.

This separation is one of the key reasons functions are reusable and independent.

What Is Scope in Python?

A namespace stores names, but it doesn't determine whether those names are accessible everywhere. That responsibility belongs to scope.

So, what is scope in Python? Scope is the region of a program where a name can be accessed directly. When Python encounters a variable, function, or class name, it searches only within the scopes available at that point in the program.

For example:

x = 10

def show():
    print(x)

show()

Here, x is defined outside the function but is still accessible inside show() because the function can access names from the global scope.

If Python cannot find a name in any accessible scope, it raises a NameError.

In short:

Namespace stores names.

Scope determines where those names are visible.

Understanding what is scope in Python is essential because it explains why the same variable name can be accessible in one place but unavailable in another.

Scope of Variables in Python

The scope of variables in Python depends on where the variable is created.

A variable defined inside a function is available only within that function, whereas a variable defined outside all functions belongs to the module and can be accessed throughout the program.

Consider the following example:

language = "Python"

def display():
    version = "3.13"
    print(language)
    print(version)

display()

Here:

language belongs to the global scope.

version belongs to the local scope.

Trying to access version outside the function results in an error because its scope ends when the function finishes execution.

Choosing the appropriate scope for variables keeps programs organized and reduces the risk of accidentally modifying data from other parts of the code.

Namespace vs Scope in Python

The terms namespace and scope are closely related, but they describe different concepts.

A namespace is where names are stored, while a scope is where those names can be accessed.

Think of a namespace as a dictionary that maps names to objects, and scope as the area of the program where Python is allowed to search that dictionary.

Namespace Scope
Stores names and their associated objects Determines where those names are accessible
Created when a module, function, or class is defined Determined by the program's structure
Organizes identifiers to avoid naming conflicts Controls name visibility during execution

For example, every function call creates a local namespace containing its own variables. However, those variables remain accessible only within the function because their scope is local.

Understanding namespace and scope in Python helps explain why variables with the same name can exist in different parts of a program without interfering with each other.

How Python Resolves Names (LEGB Rule)

How Python Resolves Names (LEGB Rule)

Whenever Python encounters a name, it doesn't search the entire program randomly. Instead, it follows a well-defined lookup order known as the LEGB rule.

LEGB stands for:

  • L – Local
  • E – Enclosing
  • G – Global
  • B – Built-in

Python searches these scopes one by one until it finds the required name. If the name isn't found in any of them, a NameError is raised.

The search order is:

Local
   ↓
Enclosing
   ↓
Global
   ↓
Built-in
   ↓
NameError

This predictable lookup mechanism ensures that Python always knows which object a particular name refers to during program execution.

Local, Enclosing, Global, and Built-in Scopes

Local Scope (L)

The local scope belongs to the currently executing function. Variables created inside a function exist only while that function is running.

def greet():
    message = "Hello"
    print(message)

greet()

Here, message is local to greet() and cannot be accessed outside the function.

Python always searches the local scope first because it contains the names most relevant to the current function.

Enclosing Scope (E)

The enclosing scope exists only in nested functions. It contains variables defined in the outer function that are accessible to the inner function.

def outer():
    language = "Python"

    def inner():
        print(language)

    inner()

outer()

Although language isn't defined inside inner(), Python finds it in the enclosing scope.

This scope enables nested functions to share data with their enclosing function.

Global Scope (G)

The global scope contains names defined at the module level.

language = "Python"

def display():
    print(language)

display()

Here, language is part of the global scope and can be accessed from any function within the same module unless a local variable with the same name shadows it.

Global variables remain available until the program finishes execution.

Built-in Scope (B)

If Python cannot find a name in the local, enclosing, or global scopes, it searches the built-in scope.

The built-in scope contains predefined names that are always available, including:

  • print()
  • len()
  • sum()
  • type()
  • range()

For example:

numbers = [10, 20, 30]
print(len(numbers))

Neither print() nor len() is defined in the program, yet Python executes them successfully because they belong to the built-in scope.

The built-in scope is the final stage of the LEGB lookup process before Python raises a NameError.

The global Keyword

By default, a variable assigned inside a function is treated as local. If you want to modify a variable defined in the global scope, you must explicitly declare it using the global keyword.

Without global, Python creates a new local variable instead of updating the existing global one.

Example:

count = 0

def increment():
    global count
    count += 1

increment()
print(count)

Output

1

Here, global count tells Python to use the variable from the global namespace rather than creating a local variable.

When Should You Use global?

Use global only when a function needs to update a module-level variable. For most situations, passing values as function arguments and returning results is a cleaner and more maintainable approach.

The nonlocal Keyword

The nonlocal keyword is used with nested functions. It allows an inner function to modify a variable defined in its enclosing function.

Unlike global, nonlocal does not refer to the global namespace. Instead, it searches for the variable in the nearest enclosing function.

Example:

def outer():
    count = 0

    def inner():
        nonlocal count
        count += 1
        print(count)

    inner()

outer()

Output

1

Here, count belongs to the enclosing function outer(). The nonlocal keyword allows inner() to modify that variable instead of creating a new local one.

global vs nonlocal

global nonlocal
Refers to a variable in the global namespace Refers to a variable in the function that is closest to it.
Used in regular and nested functions Used only inside nested functions
Updates a module-level variable Updates a variable in the enclosing scope

Assignment Determines Scope

One of Python's most important scoping rules is that assignment determines scope.

Unless it is declared as global or nonlocal, Python handles a name that is allocated anywhere within a function as a local variable.

Consider this example:

value = 100

def display():
    print(value)
    value = 200

display()

Even though print(value) comes before the value assignment in a function, Python still treats value as a local variable if it is assigned later in the function. This means that instead of printing the global value, the function gives an error.

This behavior may seem surprising, but Python determines variable scope when it compiles the function, not while executing each statement.

Understanding this rule makes it much easier to predict how Python resolves names.

Understanding UnboundLocalError

Understanding UnboundLocalError

UnboundLocalError occurs when Python expects a local variable but tries to use it before it has been assigned a value.

This error usually happens because of Python's assignment rule.

Example:

count = 10

def update():
    print(count)
    count += 1

update()

Output

UnboundLocalError: reference to the local variable "count" prior to assignment

Why does this happen?

The assignment count += 1 tells Python that count is a local variable.

When print(count) executes, the local variable hasn't been assigned yet.

Python raises an UnboundLocalError.

The correct solution is to explicitly specify which scope the variable belongs to.

count = 10

def update():
    global count
    print(count)
    count += 1

update()

Understanding UnboundLocalError is important because it explains many seemingly confusing scope-related errors in Python.

Variable Shadowing

When a variable in an inner scope and a variable in an outer scope share the same name, this is known as variable shadowing. Within its scope, the inner variable momentarily conceals the outer one.

Example:

name = "Python"

def show():
    name = "Java"
    print(name)

show()
print(name)

Output

Java
Python

The global variable is shadowed by the local variable name inside show(). The global variable doesn't change when the function is done.

Shadowing can also occur with built-in names.

For example:

list = [1, 2, 3]
print(list)

Although this code runs, the name list now refers to a list object instead of Python's built-in list() function. Attempting to call list() later in the program will result in an error because the built-in function has been shadowed.

To prevent unforeseen actions:

  • Give your variables names that are descriptive.
  • Reusing built-in names like list, dict, str, sum, or type is discouraged.
  • To minimize name conflicts, keep variables as minimal as possible.

Writing simpler code and avoiding subtle errors brought on by inadvertent name hiding are two benefits of understanding variable shadowing.

Best Practices

Understanding namespaces and scope is one step; using them effectively is another. Following these best practices helps you write cleaner, more predictable, and easier-to-maintain Python code.

1. Keep Variables in the Smallest Possible Scope

Variables should only be declared when necessary. The likelihood of unintentional changes and name conflicts is decreased by restricting the scope of a variable.

def calculate_total(price, tax):
    total = price + tax
    return total

Here, total is used only inside the function, so it remains a local variable.

2. Prefer Function Parameters Over Global Variables

Rather than depending on global variables, pass data as function parameters and return the result. As a result, functions become more reusable and testable.

def add_bonus(salary, bonus):
    return salary + bonus

This method prevents unforeseen modifications to global data.

3. Use global and nonlocal Sparingly

The global and nonlocal keywords are a part of Python, but if they are overused, the code may become harder to understand and trace. They can only be used when a variable has to be modified in different scopes.

4. Use Meaningful Variable Names

Descriptive names improve readability and reduce confusion, especially when multiple scopes are involved.

Instead of:

x = 100

Use:

employee_salary = 100

Clear names make the purpose of a variable immediately obvious.

5. Avoid Shadowing Built-in Names

Built-in names like list, dict, sum, and type are already available in Python. Reusing these variable names may conceal built-in functionality and result in unanticipated mistakes.

Select names that accurately convey your data without interfering with Python's built-in features.

Common Mistakes

Frequently, even seasoned Python programmers make errors with scope and namespaces. You can avoid perplexing problems by being aware of these typical issues.

1. Assuming Assignment Updates a Global Variable

Many beginners expect an assignment inside a function to modify a global variable automatically.

count = 10

def update():
    count = 20

Instead of updating the global variable, this creates a new local variable named count.

2. Accessing Local Variables Outside Their Scope

Variables created inside a function exist only during that function's execution.

def greet():
    message = "Hello"

print(message)

This raises a NameError because message belongs to the function's local scope.

3. Forgetting Python's LEGB Lookup Order

Python always searches names in the following order:

  • Local
  • Enclosing
  • Global
  • Built-in

Ignoring this lookup order frequently results in NameError errors or unexpected variable values.

4. Misusing global and nonlocal

Using these keywords unnecessarily increases coupling between different parts of a program and makes code more difficult to maintain.

In most cases, passing arguments and returning values produces simpler and more predictable code.

5. Shadowing Built-in Functions

Naming a variable after a built-in function can make that function unavailable later in the program.

list = [1, 2, 3]

After this assignment, list() no longer refers to Python's built-in constructor within the current scope.

Summary

Where names can be accessed is determined by scope, whereas namespaces store names. While combined, they allow Python to properly resolve variables, functions, and classes while running programs.

Python namespaces, scope, the LEGB rule, global and nonlocal keywords, assignment-based scoping, UnboundLocalError, and variable shadowing were all covered in this blog. You can develop Python code that is clearer, more consistent, and easier to maintain if you comprehend these ideas.

Frequently Asked Questions

What is a namespace in Python?

A mapping of names to the objects they relate to is called a namespace. It keeps track of variables, functions, classes, and modules so that Python can find them when the program is running.

What is scope in Python?

The area of a program where a name is accessible is defined by its scope. When running code, it establishes where Python can look for a variable, method, or class.

What distinguishes scope from namespace in Python?

Names and the objects that go with them are stored in a namespace, and their visibility and accessibility are determined by a scope. To put it simply, scope regulates the accessibility of names, whereas a namespace manages them.

What is the LEGB rule in Python?

The LEGB rule is Python's name resolution order:
L – Local
E – Enclosing
G – Global
B – Built-in
Python looks through these scopes one after the other until it locates the desired name.

When should I use the global keyword?

The global keyword should only be used when a function wants to change a variable that is defined in the global namespace. For most scenarios, passing values as function arguments is a better design choice.

What is the purpose of the nonlocal keyword?

The nonlocal keyword allows a nested function to modify a variable defined in its enclosing function. It is used only with nested functions and does not affect the global namespace.

Why does Python raise an UnboundLocalError?

UnboundLocalError occurs when Python treats a variable as local because it is assigned inside a function, but the variable is accessed before that assignment takes place.

Can two variables have the same name in Python?

Indeed. It is possible for variables in separate namespaces to share the same name without impacting one another. Python uses the LEGB rule and the current scope to decide which variable to utilize.