0Pricing
Python Academy · Lesson

nonlocal and Mutable State

Modify enclosing scope.

nonlocal and Mutable State is a free Python Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Python Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Reading vs writing enclosing variables

An inner function can freely read an enclosing variable, but if it tries to assign to that name, Python treats it as a new local variable instead. This often surprises beginners.

def outer():
    count = 0
    def inner():
        return count
    return inner()
print(outer())

The assignment trap

Assigning to an enclosing name without declaring intent makes it local, so the outer variable is never updated. Here the change is lost.

def outer():
    count = 0
    def bump():
        count = count + 1
    try:
        bump()
    except UnboundLocalError as e:
        print('Error:', e)
outer()

The nonlocal keyword

nonlocal declares that a name refers to a variable in the nearest enclosing function scope, so assignments update that outer variable instead of creating a local one.

def outer():
    count = 0
    def bump():
        nonlocal count
        count += 1
    bump()
    bump()
    return count
print(outer())

A counter closure

With nonlocal you can build a stateful counter. Each call mutates the captured variable and remembers its progress.

def make_counter():
    count = 0
    def increment():
        nonlocal count
        count += 1
        return count
    return increment
c = make_counter()
print(c())
print(c())
print(c())

Independent counters

Each counter keeps its own state because each closure captures a separate variable.

def make_counter():
    count = 0
    def inc():
        nonlocal count
        count += 1
        return count
    return inc
a = make_counter()
b = make_counter()
print(a(), a(), b())

nonlocal vs global

nonlocal targets an enclosing function scope, while global targets the module level. Use nonlocal for closures, global for module-wide state.

total = 0
def add_global(n):
    global total
    total += n
add_global(5)
add_global(3)
print(total)

Mutable state without nonlocal

If the enclosing value is a mutable object like a list or dict, you can mutate it in place without nonlocal, because you are not rebinding the name.

def make_logger():
    history = []
    def log(msg):
        history.append(msg)
        return history
    return log
log = make_logger()
log('start')
print(log('stop'))

An accumulator

Combining nonlocal with a closure gives a running accumulator that adds each value to a remembered total.

def make_accumulator():
    total = 0
    def add(n):
        nonlocal total
        total += n
        return total
    return add
acc = make_accumulator()
print(acc(10))
print(acc(5))
print(acc(100))

A simple toggle

nonlocal makes it easy to flip a boolean state between calls.

def make_toggle():
    on = False
    def toggle():
        nonlocal on
        on = not on
        return on
    return toggle
t = make_toggle()
print(t(), t(), t())

Resetting state

You can expose multiple inner functions that share the same enclosing state, for example one to add and one to reset.

def make_bank():
    balance = 0
    def deposit(n):
        nonlocal balance
        balance += n
        return balance
    def reset():
        nonlocal balance
        balance = 0
        return balance
    return deposit, reset
deposit, reset = make_bank()
print(deposit(50))
print(reset())

nonlocal needs an existing variable

nonlocal requires the name to already exist in an enclosing scope; otherwise Python raises a SyntaxError at definition time. It cannot create a new outer variable.

def outer():
    value = 'set'
    def inner():
        nonlocal value
        value = 'changed'
    inner()
    return value
print(outer())

Quick Check

Inside a nested function you want to reassign a variable from the enclosing function. Which keyword do you use?

Recap: nonlocal and Mutable State

Summary:

  • Assigning to an enclosing name normally creates a new local; nonlocal prevents that.
  • nonlocal targets the enclosing function scope; global targets the module.
  • Mutating a mutable object in place needs no declaration.
  • Closures with nonlocal build counters, accumulators, and toggles that remember state.
def make_counter():
    n = 0
    def inc():
        nonlocal n
        n += 1
        return n
    return inc
c = make_counter()
print(c(), c())

Frequently asked questions

Is the “nonlocal and Mutable State” lesson free?

Yes — the full text of “nonlocal and Mutable State” is free to read here on the web, and the Python Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Python Academy course, upgrade to CoddyKit PRO.

What will I learn in “nonlocal and Mutable State”?

Modify enclosing scope. You practise Python Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Python Academy?

No prior experience is required. Python Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “nonlocal and Mutable State” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Python Academy lesson?

Yes. Every Python Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. First-Class Functions
  2. Closures and Free Variables
  3. nonlocal and Mutable State
  4. Partial Functions
← Back to Python Academy