Using nonlocal in Nested Functions

Data Science Tool Box Mar 18, 2026

Using nonlocal in Nested Functions

The nonlocal keyword allows a nested (inner) function to modify a variable from its enclosing (outer) function’s scope. While not used as frequently as global, it is very useful in specific data science scenarios such as creating counters, accumulators, or maintaining state within nested helper functions.

TL;DR — When to Use nonlocal

  • Use nonlocal when a nested function needs to modify a variable defined in the enclosing function
  • Do not use it to modify global variables (use global for that)
  • Most common use cases: counters, accumulators, and stateful nested functions

1. Basic Example of nonlocal

def process_batch(transactions):
    total_revenue = 0.0          # Variable in enclosing scope
    
    def add_transaction(amount: float):
        nonlocal total_revenue   # Allows inner function to modify outer variable
        total_revenue += amount
    
    for tx in transactions:
        add_transaction(tx["amount"])
    
    return total_revenue


# Usage
batch = [{"amount": 1250.75}, {"amount": 890.50}, {"amount": 2340.00}]
revenue = process_batch(batch)
print(f"Total revenue: {revenue}")

2. Real-World Data Science Example

def calculate_running_metrics(df):
    """Calculate running statistics using nested function with nonlocal."""
    running_sum = 0.0
    running_count = 0
    
    def update_running_stats(amount: float):
        nonlocal running_sum, running_count
        running_sum += amount
        running_count += 1
        return running_sum / running_count if running_count > 0 else 0
    
    df = df.copy()
    df["running_avg"] = df["amount"].apply(update_running_stats)
    
    return df

3. Best Practices in 2026

  • Use nonlocal only when you need the inner function to modify a variable from the outer scope
  • Prefer returning values instead of using nonlocal when possible (more functional style)
  • Limit the use of nonlocal to small, well-contained nested functions
  • Document clearly when a nested function modifies outer variables using nonlocal
  • Be cautious — overuse of nonlocal can make code harder to understand and debug

What nonlocal Does

nonlocal tells Python that a name in a nested function refers to a variable in the nearest enclosing function, not a new local one. Without it, assigning to that name inside the inner function creates a separate local variable.

It is most useful for small stateful closures such as counters or running totals. For anything with several pieces of state, a class is usually clearer.

Worked Example: A Running Total

A closure keeps a running total across calls using nonlocal.

def make_accumulator():
    total = 0.0

    def add(value: float) -> float:
        nonlocal total
        total += value
        return total

    return add

acc = make_accumulator()
for v in [10.0, 5.5, 4.5]:
    print(acc(v))

other = make_accumulator()
print(other(1.0))

Each call adds to the same total, giving 10.0, 15.5 and 20.0. A second accumulator starts from zero, because every call to the factory creates a new enclosing scope.

Practical Tips

The difference between global, nonlocal and local names is easy to test with small experiments. Try removing the nonlocal line in the example: Python will raise an UnboundLocalError on the first call, because total += value tries to read a local name before assigning it.

In data pipelines, nonlocal closures can count processed records or keep the last seen value in a stream. Once you need to reset, inspect or serialise the state, rewrite the closure as a small class with clear attributes.

Remember that nonlocal only works for names that already exist in an enclosing function. It cannot create a new variable, and it cannot reach module-level names; those need the global keyword instead. Keeping the state small makes such closures easy to reason about.

Common Mistakes

  • Using global when nonlocal is meant.
  • Forgetting nonlocal and getting an UnboundLocalError.
  • Keeping lots of state in closures instead of a class.
  • Using nonlocal at module level, which is a syntax error.

Quick Summary

  • nonlocal targets the enclosing function scope.
  • It allows assignment to outer variables.
  • Good for counters and running totals.
  • Use a class for complex state.

Conclusion

The nonlocal keyword is a specialized tool that allows nested functions to modify variables from their enclosing scope. In data science, it is most useful for creating accumulators, counters, or maintaining state within a parent function. However, in most cases, returning values explicitly is cleaner and more maintainable than relying on nonlocal.

Next steps:

  • Review your current code and identify any nested functions that modify outer variables. Consider whether using nonlocal is the best approach or if returning values would be cleaner.

Related Tutorials

Share:
Last updated: March 2026
Built-in Functions in Python 2026 str() in Python 2026: String Conversion + Modern Formatting & Best Practices memoryview with JAX in Python 2026: Zero-Copy NumPy → JAX Array Interop + Efficient ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + GPU Pinning & ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + ML Examples memoryview with NumPy & PyTorch in Python 2026: Zero-Copy Views, Efficient Slicing & ML Interop Examples memoryview with NumPy in Python 2026: Zero-Copy Views, Efficient Slicing & Real ML Examples memoryview() in Python 2026: Zero-Copy Magic for Large Binary Data + Real Examples import() in Python 2026: How It Works, Security Risks & Modern Alternatives (importlib) zip() in Python 2026: How to Use It, strict=True Behavior & Real-World Examples vars() in Python 2026: Accessing Object Namespace + Modern Introspection Patterns type() in Python 2026: Dynamic Type Inspection & Object Creation + Modern Patterns tuple() in Python 2026: Immutable Sequences + Modern Patterns & Best Practices super() in Python 2026: Method Resolution & Modern Inheritance Patterns sum() in Python 2026: Summing Iterables + Modern Numeric Patterns & Best Practices staticmethod() in Python 2026: Static Methods, Utility Functions & Modern Best Practices sorted() in Python 2026: Sorting Iterables + Modern Patterns & Best Practices slice() in Python 2026: Creating Slice Objects + Modern Patterns & Best Practices setattr() in Python 2026: Dynamic Attribute Setting + Modern Patterns & Safety set() in Python 2026: Mutable Sets Creation + Modern Patterns & Best Practices