Python Closures
1Python Closures: overview
A closure is a function that remembers the variables of the enclosing function where it was created, so it can keep using them even after that outer function has returned.
Before this lesson: Python Functions, Python Variable Scope
2Python Closures: diagram
3Python Closures: explained step by step
In the scope lesson you saw that a function defined inside another function can read the outer function's variables. This lesson is about what happens when that inner function leaves the place where it was born. Python keeps the variables it needs alive, and the result is a closure: a function bundled together with the values it remembers from the scope around it.
A function that remembers
def make_greeter(greeting):
def greet(name):
return f"{greeting}, {name}!"
return greet # return the function itself: no parentheses
hello = make_greeter("Hello")
hola = make_greeter("Hola")
print(hello("Asha"))
print(hola("Ben"))make_greeter is finished as soon as it returns, and normally its local variables would disappear with it. But greet still needs greeting, so Python keeps that value alive and attaches it to the function. hello and hola are two separate greet functions, each remembering its own greeting.
Pay attention to the last line of make_greeter. return greet hands back the function. return greet("x") would call it and hand back the text instead.
Free variables
Inside greet, the name name is local because it is a parameter. But greeting is neither local nor global: it belongs to the enclosing function. A name used in a function but defined in an enclosing scope is called a free variable, and a function with at least one free variable is a closure.
Function factories
A function that builds and returns other functions is a factory. You give it the settings once, and it hands back a specialised function you can call as often as you like:
def make_multiplier(factor):
def multiply(number):
return number * factor
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(10), triple(10))
print(list(map(triple, [1, 2, 3])))Because triple takes one argument, it plugs straight into map(), filter() or sorted(key=...). A lambda that uses an outer variable, like lambda n: n * factor, is a closure too.
State that survives between calls
A closure can also change what it remembers, which gives you a function with a memory. Because count += 1 is an assignment, Python would normally treat count as a new local variable and fail. The nonlocal keyword says "this name belongs to the enclosing function":
def make_counter():
count = 0
def step():
nonlocal count # change the enclosing variable, do not make a new one
count += 1
return count
return step
page_views = make_counter()
downloads = make_counter()
print(page_views(), page_views(), page_views())
print(downloads())Each call to make_counter() creates a fresh count, so the two counters never disturb each other. You only need nonlocal when you reassign the name. Changing an object it points to, such as scores.append(80) on a remembered list, needs no keyword.
Closure or class?
A class can hold the same state, and sometimes it is the better choice:
class Counter:
def __init__(self):
self.count = 0
def step(self):
self.count += 1
return self.count
page_views = Counter()
print(page_views.step(), page_views.step())
print(page_views.count) # a class lets anyone read the stateA closure suits one small behaviour with a little private state, and nobody outside can touch count. Write a class once you need several methods, attributes people should read, or something other developers will extend.
Why not just use a global variable?
A global count would also survive between calls, but there is only one of it. Two counters would need two globals and two almost identical functions, and any code anywhere could change them by accident. A closure gives every function its own private copy, created on demand.
Peeking inside with __closure__
Python stores each remembered variable in a small cell. You will rarely need this outside of learning and debugging, but it makes the idea concrete:
def make_counter():
count = 0
def step():
nonlocal count
count += 1
return count
return step
tally = make_counter()
tally()
tally()
print(tally.__code__.co_freevars)
cell = tally.__closure__[0]
print(cell.cell_contents)
tally()
print(cell.cell_contents)
def plain(x):
return x
print(plain.__closure__)co_freevars lists the remembered names, and cell_contents shows the live value. The cell changes when the function changes it. An ordinary function with no free variables has __closure__ set to None.
The loop gotcha: late binding
A closure remembers the variable, not the value the variable had when the function was created. In a loop, every function shares the same variable, and by the time you call them it holds the last value:
def make_greeters(names):
late = []
early = []
for name in names:
late.append(lambda: f"Hi {name}")
early.append(lambda name=name: f"Hi {name}")
return late, early
late, early = make_greeters(["Asha", "Ben", "Cara"])
print([g() for g in late])
print([g() for g in early])The first list says Cara three times. The second works because a default argument is evaluated when the lambda is created, so each function gets its own copy. Another fix is to build each function inside a small factory, so every call has its own variable.
4Python Closures: syntax
def outer(setting): # outer function: its variables can be remembered
state = 0
def inner(value): # inner function: a closure if it uses outer's names
nonlocal state # only needed to REASSIGN an enclosing variable
state += value
return value * setting # setting is a free variable
return inner # return the function itself, no parentheses
func = outer(3)
func(10) # state and setting live on between calls
func.__code__.co_freevars # names the closure remembers
func.__closure__[0].cell_contents # peek at one remembered value
5Python Closures: run the example
Press Run (or Ctrl + Enter). The output (step 6) appears directly below the code. Change anything and run it again.
def make_ticket_machine(prefix):
number = 0
def next_ticket():
nonlocal number
number += 1
return f"{prefix}-{number:03d}"
return next_ticket
help_desk = make_ticket_machine("HELP")
billing = make_ticket_machine("BILL")
print(help_desk())
print(help_desk())
print(billing())
print(help_desk())Expected outputHELP-001
HELP-002
BILL-001
HELP-003
7Python Closures: how the example works
Hover or tap a step to highlight its lines in the example above.
- Line 1-2
make_ticket_machineis a factory. Each call gets its ownprefixand its ownnumber, both local to that call, andnumberstarts at0. - Line 3
next_ticketis defined inside the factory. It usesprefixandnumberwithout receiving them as parameters, so they are free variables andnext_ticketis a closure. - Line 4
nonlocal numberis required because line 5 reassigns the name. Without it, Python would treatnumberas a new local variable and raise anUnboundLocalError. - Line 5-6Each call adds one to the remembered
numberand builds a ticket such asHELP-001.:03dpads the number with zeros to three digits. - Line 7The factory returns the function itself, with no parentheses. After this line
make_ticket_machinehas finished, yet the function still remembers its two variables. - Line 9-10Two separate calls create two separate closures.
help_deskremembers"HELP"and its own count, andbillingremembers"BILL"and a different count. - Line 11-14
help_deskcounts1,2, then3, whilebillingstarts from1in the middle. The two counters never affect each other.
8Python Closures: common mistakes
Changing a remembered variable without nonlocal
✗ Wrong
def make_counter(start):
count = start
def increment():
count += 1
return count
return increment
clicks = make_counter(0)
print(clicks())✓ Right
def make_counter(start):
count = start
def increment():
nonlocal count
count += 1
return count
return increment
clicks = make_counter(0)
print(clicks())count += 1 assigns to count, so Python decides count is a local variable of increment, and that local has no value yet. Declare nonlocal count first, so the name refers to the enclosing variable.
Calling the inner function instead of returning it
✗ Wrong
def make_multiplier(factor):
def multiply(number):
return number * factor
return multiply(1)
double = make_multiplier(2)
print(double(10))✓ Right
def make_multiplier(factor):
def multiply(number):
return number * factor
return multiply
double = make_multiplier(2)
print(double(10))multiply(1) runs the function right away and returns a number, so double is just 2. To return the function itself, leave off the parentheses.
Creating functions in a loop that all share one variable
✗ Wrong
def make_buttons(labels):
buttons = []
for label in labels:
buttons.append(lambda: "clicked " + label)
return buttons
for button in make_buttons(["Save", "Open", "Exit"]):
print(button())✓ Right
def make_buttons(labels):
buttons = []
for label in labels:
buttons.append(lambda label=label: "clicked " + label)
return buttons
for button in make_buttons(["Save", "Open", "Exit"]):
print(button())Every lambda remembers the same variable label, and after the loop it holds "Exit". Freeze the current value with a default argument (lambda label=label: ...) or build each function in a small factory.
Using nonlocal for a global variable
✗ Wrong
total = 0
def add(amount):
nonlocal total
total += amount
add(5)
print(total)✓ Right
def make_total():
total = 0
def add(amount):
nonlocal total
total += amount
return total
return add
add = make_total()
add(5)
print(add(3))nonlocal only works for a variable of an enclosing function. A top-level variable is global. Better still, keep the state in a closure so nothing global is needed.
Calling the factory again for every use
✗ Wrong
def make_counter():
count = 0
def step():
nonlocal count
count += 1
return count
return step
print(make_counter()())
print(make_counter()())
print(make_counter()())✓ Right
def make_counter():
count = 0
def step():
nonlocal count
count += 1
return count
return step
counter = make_counter()
print(counter())
print(counter())
print(counter())Every make_counter() call builds a brand-new counter with its own count. Call the factory once, keep the returned function in a variable, and call that variable each time.
9Python Closures: exercises
Write your answer, then press Check. The checks run in your browser.
1. Build an adder factory
Write code Easy ✓ CompletedWrite a function make_adder(amount) that returns a new function. The returned function takes one number and returns that number plus amount. Then create add_ten = make_adder(10) and print add_ten(5).
def make_adder(amount):
# define an inner function that adds amount, and return it
pass
add_ten = make_adder(10)
print(add_ten(5))
make_adder that uses amount, then finish with return followed by that function's name, without parentheses.def make_adder(amount):
def add(number):
return number + amount
return add
add_ten = make_adder(10)
print(add_ten(5))
2. Predict the late-binding prices
Predict the output Medium ✓ CompletedTwo lists of functions are built in a loop. One list uses plain lambdas, the other freezes the value with a default argument. After the loop the variable discount is changed once more. Type exactly what the program prints.
def make_prices():
shown = []
frozen = []
for discount in [10, 20, 30]:
shown.append(lambda: 100 - discount)
frozen.append(lambda discount=discount: 100 - discount)
discount = 50
return shown, frozen
shown, frozen = make_prices()
print([f() for f in shown])
print([f() for f in frozen])
discount when they are called, and by then it holds 50. The default argument was evaluated when each lambda was created.[50, 50, 50]
[90, 80, 70]
2 more exercises → 6 more examples
Questions learners ask
What is the difference between nonlocal and global in Python?
global makes a name refer to a variable at the top level of the file, while nonlocal makes it refer to a variable of the nearest enclosing function. Both are only needed when you assign to the name. Using nonlocal for something that is not in an enclosing function is a SyntaxError.
Is every inner function or lambda a closure?
No. It becomes a closure only if it uses a variable from an enclosing function. A nested function that uses only its own parameters has closure set to None. You can check any function by printing func.closure: a tuple of cells means it is a closure, and None means it is not.
Should I use a closure or a class to keep state between calls?
Use a closure when you need one small behaviour with a little private state, such as a counter or a configured helper. Use a class when you need several related methods, attributes that other code should read, or objects that people will extend. The class is usually clearer as the program grows.
Further reading
10Next lesson
Up nextPython DecoratorsA decorator is a function that takes another function, wraps extra behaviour around it and returns the wrapped version;…Related:
Python Lambda FunctionsPython RecursionPython Variable ScopePython Decorators
← Python Variable Scope · All Functions and Modules lessons · Practise in the IDE