Swiftorial Logo
Home
Swift Lessons
Tutorials
Learn More
Career
Resources

Python FAQ: Top Questions

5. What are Python namespaces?

In Python, a **namespace** is a mapping from names (identifiers) to objects. Every name (variable, function, class, module, etc.) lives in a namespace. Think of it as a dictionary where keys are names and values are the corresponding objects. Namespaces are crucial for preventing naming conflicts in a program, especially as codebases grow larger or when integrating code from multiple modules.

Python manages several distinct namespaces, each with its own scope, which determines where a name can be accessed. The lookup order for names follows the **LEGB Rule**:

  • L: Local Namespace:
    • This is the innermost namespace, created when a function is called. It contains all the names defined inside that function (e.g., function parameters, variables defined within the function body).
    • This namespace is temporary and is destroyed once the function completes its execution.
  • E: Enclosing (or Nonlocal) Namespace:
    • This namespace exists for nested functions. If an inner (nested) function doesn't find a name in its local scope, it looks in the scope of its enclosing function(s).
    • Names in this scope can be accessed by nested functions, and modified using the `nonlocal` keyword.
  • G: Global Namespace:
    • This namespace is created when a module (a `.py` file) is loaded. It contains all names defined at the top level of that module (e.g., global variables, functions, classes, and imported modules).
    • This namespace exists as long as the module is in memory.
  • B: Built-in Namespace:
    • This is the outermost namespace, containing all of Python's built-in functions (like `print()`, `len()`, `type()`), built-in exceptions, and other built-in objects.
    • This namespace is created when the Python interpreter starts and is available globally throughout your program.

When Python encounters a name, it searches for it in these namespaces in the order: **Local → Enclosing → Global → Built-in (LEGB)**. The first definition it finds is the one used. If a name is not found in any of these namespaces, a `NameError` is raised.

This systematic lookup ensures that names defined in more specific (local) scopes take precedence over names in broader (global or built-in) scopes, preventing unintended overwrites and maintaining modularity.


# --- Example 1: Demonstrating LEGB Rule ---

# Global namespace (G)
global_var = "I am a global variable"

def outer_function():
    # Enclosing namespace (E) for inner_function
    enclosing_var = "I am an enclosing variable"

    def inner_function():
        # Local namespace (L)
        local_var = "I am a local variable"

        # Accessing names in order: L -> E -> G -> B
        print(f"Inside inner_function:")
        print(f"  Local variable: {local_var}") # Found in L
        print(f"  Enclosing variable: {enclosing_var}") # Found in E
        print(f"  Global variable: {global_var}") # Found in G
        print(f"  Built-in function (len): {len('test')}") # Found in B (Built-in)

        # What if a variable with the same name exists in multiple scopes?
        # Python uses the closest scope first.
        x = "local_x"
        print(f"  x in local_scope: {x}") # Uses local_x

    # Calling the inner function
    inner_function()

    # Attempt to access inner_function's local_var (will fail)
    # print(local_var) # This would raise a NameError

# Calling the outer function
print("--- Calling outer_function ---")
outer_function()
print("--- After outer_function call ---")

# Accessing global_var outside functions
print(f"Outside functions, global_var: {global_var}")

# --- Example 2: Using 'global' and 'nonlocal' keywords ---

g = 10 # Global variable

def modify_scopes():
    e = 20 # Enclosing variable

    def modify_inner():
        l = 30 # Local variable

        # Modify global variable 'g'
        global g
        g = 100
        print(f"  Inside modify_inner: g (global) changed to {g}")

        # Modify enclosing variable 'e'
        nonlocal e
        e = 200
        print(f"  Inside modify_inner: e (enclosing) changed to {e}")

        # 'l' remains local
        l = 300
        print(f"  Inside modify_inner: l (local) changed to {l}")

    print(f"Before modify_inner: g={g}, e={e}")
    modify_inner()
    print(f"After modify_inner: g={g}, e={e}") # e is modified here

print(f"Initial global g: {g}")
modify_scopes()
print(f"Final global g: {g}") # g is modified here
          

Explanation of the Example Code:

  • **LEGB Rule Demonstration:**
    • We define `global_var` in the **Global** scope.
    • `outer_function` defines `enclosing_var`, which is in its **Enclosing** scope relative to `inner_function`.
    • `inner_function` defines `local_var`, which is in its **Local** scope.
    • Inside `inner_function`, when `print(local_var)` is called, Python finds `local_var` in its own local scope.
    • When `print(enclosing_var)` is called, Python doesn't find `enclosing_var` locally, so it looks in the enclosing `outer_function`'s scope and finds it there.
    • When `print(global_var)` is called, it's not local or enclosing, so Python finds it in the module's global scope.
    • `len()` is a built-in function, so it's found in the Built-in namespace as a last resort.
    • The example also shows that `local_var` defined inside `inner_function` is not accessible outside it, even within `outer_function`, demonstrating the temporary nature of local namespaces.
  • **`global` and `nonlocal` Keywords:**
    • The `global` keyword explicitly tells Python that the variable being assigned (`g = 100`) refers to the variable in the module's global namespace, not a new local variable. This allows modification of a global variable from inside a function.
    • The `nonlocal` keyword (introduced in Python 3) is used in nested functions to indicate that a variable being assigned (`e = 200`) refers to a variable in the nearest enclosing scope that is not global. This allows inner functions to modify variables in their immediate outer (non-global) function's scope.
    • Notice that `l` remains a local variable to `modify_inner` because neither `global` nor `nonlocal` was used, so `l = 300` creates a *new* local variable `l` that shadows the previous `l=30`.

Understanding namespaces and the LEGB rule is critical for predicting how Python resolves variable names and for avoiding common bugs related to variable scope, especially in larger applications with multiple functions and modules.