TechSchool
Home / Python3 / Python Closures

Python Closures

This page explains Python closures and shows how an inner function keeps a variable from the outer function. It gives runnable examples for the counter factory, the late binding trap, and the nonlocal keyword.

What a Closure Is

A closure is an inner function that reads a variable from the outer function. The inner function keeps the variable after the outer function returns.

The outer function must return the inner function. The inner function must use a name from the outer function. Python stores that name in the closure.

The example below builds a counter factory. Each call to make_counter returns a new function. The function adds one to its own count.

def make_counter():
    count = 0
    def step():
        nonlocal count
        count += 1
        return count
    return step

c = make_counter()
print(c(), c(), c())

Inspect a Closure

The attribute __closure__ holds the cells of the closure. Each cell holds one value from the outer function. The attribute co_freevars lists the names of those cells.

The example below shows both attributes. The name count appears in co_freevars. The cell value is 0 before the first call.

You can read the cell value with cell_contents. This helps you debug a closure. Do not change the cell value in normal code.

def make_counter():
    count = 0
    def step():
        nonlocal count
        count += 1
        return count
    return step

c = make_counter()
print(c.__code__.co_freevars)
print(c.__closure__[0].cell_contents)
c()
print(c.__closure__[0].cell_contents)

The Late Binding Trap

A function looks up a free variable when you call it. It does not copy the value when you create it. Functions made in a loop all see the last value of the loop variable.

The first example below shows the mistake. All three functions return 2. The loop variable i is 2 at the end of the loop.

The fix is a default argument. Python reads a default argument when you create the function. The second example binds i at each step. The output is 0, 1, 2.

funcs = []
for i in range(3):
    funcs.append(lambda: i)
print([f() for f in funcs])

fixed = []
for i in range(3):
    fixed.append(lambda i=i: i)
print([f() for f in fixed])

A Factory as the Fix

A factory function is another fix for late binding. The factory takes the value as an argument. The inner function closes over that argument.

Each call to the factory makes a new scope. The scope holds one value. The inner function cannot see the loop variable.

The example below uses a factory. The output is 0, 1, 2. The factory also makes the code easy to read.

def make_func(value):
    def inner():
        return value
    return inner

funcs = [make_func(i) for i in range(3)]
print([f() for f in funcs])

Write with nonlocal

A closure can read an outer variable without a keyword. A write needs the nonlocal keyword. Without nonlocal, Python makes a new local variable.

The first example below shows the mistake. It raises UnboundLocalError. The name count is local to step, and step reads it before the assignment.

The fix is the nonlocal statement. It tells Python to use the count from the outer function. The counter then works as expected.

def make_counter():
    count = 0
    def step():
        count += 1
        return count
    return step

c = make_counter()
print(c())

Closure or Class

A closure is simpler when you need one small function with one or two saved values. It needs less code than a class. It has no self and no method names.

A class is better when you need many methods or many attributes. A class is also better when other code must read or change the state. A class gives clear names to that state.

Use a closure for a short callback or a small factory. Use a class for a larger object with a public interface.

Common questions

What is the difference between a closure and a global?

A closure reads a name from the outer function scope. A global reads a name from the module scope. A closure keeps one value for each call of the outer function.

Why do lambdas in a loop return the same value?

A lambda looks up the loop variable when you call it. By then the loop has ended, so every lambda sees the last value. Use a default argument or a factory to bind the value early.

Can a closure change an outer variable?

Yes, but only with the nonlocal keyword. Without nonlocal, an assignment makes a new local variable and can raise UnboundLocalError.

Continue learning
Python decorators Python3 iterator generator Python3 class