Legends ofPythos
Claim your name
Writing Real Programs

Closures and nonlocal

Lesson 7 of 16

Watch the lesson1:01 · with Sigrid
In Python a function is a value, like a number or a list. You can store one in a variable, pass one to another function, and even make one inside another function and hand it back.

Functions that make functions

def make_adder(n):
    def add(x):
        return x + n
    return add

add_five = make_adder(5)
add_ten = make_adder(10)
print(add_five(1))   # 6
print(add_ten(1))    # 11
Two closures from one factory
make_adder builds a new function called add and returns it. When make_adder finishes, its variable n would normally be gone, but add still needs it, so Python keeps it attached to the returned function. A function that carries variables from the function that made it is a closure.

Changing a remembered variable: nonlocal

Reading an outer variable just works. Assigning to it does not: inside bump, count = count + 1 would make a new local count and fail. nonlocal count tells Python you mean the one in the enclosing function.
def make_counter():
    count = 0
    def bump():
        nonlocal count
        count += 1
        return count
    return bump

tally = make_counter()
tally()
tally()
print(tally())   # 3
A counter that remembers
Compare it with global from the previous lesson: global reaches the module's top level, while nonlocal reaches into the functions this one was made inside, nearest first.

How Python finds a name: LEGB

When your code uses a name, Python looks in four places, in this order, and takes the first one it finds:
  1. Local: the current function.
  2. Enclosing: any function the current one was made inside.
  3. Global: the top level of the module.
  4. Built-in: names Python always has, such as len and print.

Your turn

0 of 3 solved

Exercise 1

+35 XP
Write a factory make_multiplier(factor) that returns a new function. The returned function takes one argument x and returns x * factor. Then make double and triple with it. For example, double(5) is 10.
def make_multiplier(factor):
    # define an inner function that uses factor, and return it
    pass




double = None  # make these with make_multiplier
triple = None

Run your code to check it against the tests.

Exercise 2

+35 XP
Write make_counter(), which returns a function. Each call to that function returns how many times it has been called so far, starting at 1. Keep the count in the enclosing function and use nonlocal to change it.
def make_counter():
    # keep a count here and return an inner function that bumps it
    pass

Run your code to check it against the tests.

Exercise 3

+35 XP
Write make_account(balance), which returns a function move(amount). move adds amount to the balance kept in the enclosing function (a negative amount is a withdrawal) and returns the new balance. A withdrawal that would take the balance below zero raises ValueError and leaves the balance as it was.
def make_account(balance):
    # return an inner move(amount) that updates balance with nonlocal
    pass

Run your code to check it against the tests.