Python: Nonlocal Variables

In Python, a nonlocal variable is one that comes from an outer (but not global) function. When you use the nonlocal keyword, you tell Python, “Hey, I want to change that outer variable, not create a new one.”

Why does this matter? Because sometimes, you want a nested function to remember and update something — like a counter, a score, or a progress bar — without using global variables or building a class.

Let’s see a sweet little example from a bakery:

def bakery():

    cakes = 0  # This variable lives in the outer function

    def bake():

        nonlocal cakes  # Reach back and update the outer variable
        cakes += 1

        return f"Baked {cakes} cake(s)!"

    return bake

Here, bake() remembers the number of cakes already baked and adds one more each time it’s called. The nonlocal keyword gives it permission to update the cakes variable defined in bakery().

Let’s call it in action:

oven = bakery()

print(oven())  # Baked 1 cake(s)!
print(oven())  # Baked 2 cake(s)!

Each time you call oven(), it bakes one more cake — and remembers the total. That’s the magic of nonlocal.

How to Declare and Use nonlocal

Using nonlocal in Python is simple once you get the hang of it. First, you define a variable inside an outer function. Then, inside a nested (inner) function, you use the nonlocal keyword to tell Python that you want to modify the outer variable, not create a new one.

Let’s try a fun example with a dog learning tricks. We’ll make a trainer function that keeps track of how many tricks the dog knows:

def dog_trainer():

    tricks = 0  # This belongs to the outer function

    def teach():

        nonlocal tricks  # This lets us change 'tricks' from inside
        tricks += 1

        return f"The dog knows {tricks} trick(s)!"

    return teach

When you call dog_trainer(), it gives you a teach() function that remembers how many tricks the dog has learned so far. Every time you call it, the dog learns one more:

buddy = dog_trainer()

print(buddy())  # The dog knows 1 trick(s)!
print(buddy())  # The dog knows 2 trick(s)!

Without nonlocal, Python would assume you’re making a brand-new tricks variable inside teach() — and the count wouldn’t go up. With nonlocal, you can build fun, memory-holding functions like this easily.

Comparing nonlocal vs Regular Variables

Let’s see why the nonlocal keyword is so important. Without it, Python will treat a variable inside a nested function as brand new — even if it has the same name as one from the outer function. This can lead to confusing errors or unexpected behavior.

Take this silly cat example. We’re trying to count meows:

def cat_counter():

    meows = 0

    def add_meow():

        meows = meows + 1  # Uh-oh! This causes an error
        return meows

    return add_meow

At first glance, it looks like the inner add_meow() function is trying to update meows. But since we didn’t tell Python we want to use the outer meows, it thinks we’re making a new one — and tries to read it before it exists. That causes an UnboundLocalError.

Now let’s fix it with nonlocal:

def cat_counter():

    meows = 0

    def add_meow():

        nonlocal meows  # Tell Python: use the outer 'meows'
        meows += 1

        return meows

    return add_meow

Now, everything works as expected. The inner function updates the meows count from the outer function, and every call increases it:

luna = cat_counter()

print(luna())  # 1
print(luna())  # 2

This is the magic of nonlocal. It gives your inner function access to change variables that would otherwise be out of reach.

Tracking State with nonlocal

One of the best ways to use nonlocal is to keep track of state — like counting, storing progress, or recording changes — all without using global variables or classes. You can think of it like giving your nested function a memory.

Let’s use a fun example with a potion mixer. Imagine you’re adding magical drops to a potion, and you want to keep count:

def potion():

    drops = 0

    def add_drop():

        nonlocal drops
        drops += 1

        return f"{drops} magical drops added."

    return add_drop

In this case, potion() sets up a private space with a drops counter. The inner add_drop() function doesn’t forget that number — it updates it every time it’s called.

Here’s what using it looks like:

brew = potion()

print(brew())  # 1 magical drops added.
print(brew())  # 2 magical drops added.

Even though the outer function has finished running, the drops variable is still alive inside brew. That’s the power of nonlocal — it keeps your data tucked safely inside, while still letting you update it.

nonlocal in Loops and Closures

The nonlocal keyword also works great inside loops and closures, helping you keep track of changes as things happen step-by-step.

Imagine a simple game where a player collects treasures. You want to count how many treasures they find, updating the total every time:

def player():

    treasures = 0

    def find():

        nonlocal treasures
        treasures += 1

        return f"You now have {treasures} treasures!"

    return find

# Create a player
adventurer = player()

# Simulate finding treasures in a loop
for _ in range(5):
    print(adventurer())

Here, the loop calls the find() function multiple times, and nonlocal allows find() to update the treasures count each time, remembering the total across all calls. This shows how closures combined with nonlocal can keep and modify state even when used repeatedly in loops.

This setup lets you use nonlocal to maintain state in a clean, simple way — perfect for games, trackers, or any process that needs to remember past actions inside loops or repeated calls.

Creative Uses of nonlocal

The nonlocal keyword shines when you want your functions to remember and update information over time. For example, you can build a personalized greeter that keeps track of how many times it greeted someone, a task progress updater that counts steps completed, or a scorekeeper for games.

Imagine a dragon that remembers how many times it has flown. Each time you call its fly() function, it updates the flight count using nonlocal and reports the total flights so far:

def dragon():

    flights = 0  # This variable keeps track of flights made

    def fly():

        nonlocal flights  # Allow modification of the outer flights variable
        flights += 1

        return f"The dragon has flown {flights} times!"

    return fly

# Create a dragon instance
smaug = dragon()

# Make the dragon fly several times
print(smaug())  # The dragon has flown 1 times!
print(smaug())  # The dragon has flown 2 times!
print(smaug())  # The dragon has flown 3 times!

This simple use of nonlocal lets the inner function update the flights variable in the enclosing scope, making your code flexible and stateful without global variables or complex classes.

Tips for Using nonlocal Clearly

When using nonlocal, keep your variable names meaningful so it’s easy to understand what each variable does. Use nonlocal only when it’s truly needed — that is, when an inner function must remember and update a value from an outer function. Also, name your functions and variables to clearly show what state they are managing, like score, counter, or progress. This way, your code stays clean and easy to follow.

Final Example: A Magic Pet that Learns Tricks

Let’s bring everything together with a fun and complete example: a magic pet that learns new tricks and remembers them. This example shows how to use the nonlocal keyword to keep track of the tricks the pet has learned over time.

We start by defining the magic_pet function. Inside, we create an empty list called tricks to store the pet’s learned tricks. Then, we define the inner function teach that takes a new trick as an argument. Using the nonlocal keyword, teach updates the outer tricks list by appending the new trick. Finally, it returns a message showing all the tricks the pet has learned so far.

Here’s the code:

def magic_pet(name):

    tricks = []

    def teach(trick):

        nonlocal tricks
        tricks.append(trick)

        return f"{name} has learned: {', '.join(tricks)}"

    return teach

spark = magic_pet("Spark")

print(spark("roll over"))
print(spark("fetch fireball"))

When you run this code, the first call to spark("roll over") adds “roll over” to the list and shows that Spark has learned it. The second call to spark("fetch fireball") adds another trick, and Spark proudly shows off both skills. Thanks to the nonlocal keyword, the teach function remembers all past tricks without needing global variables or classes.

This example perfectly demonstrates how nonlocal lets inner functions keep and modify state from their outer scope, making your Python code flexible and fun to write.

Conclusion

The nonlocal keyword gives your inner functions the ability to update variables from their outer enclosing scope in a safe and clean way. This lets you create counters, trackers, and little helpers that can remember things without relying on global variables or complex classes. Using nonlocal keeps your Python code neat and clever, making it easier to write functions that manage their own state and bring your programs to life.