Closures are a powerful concept in programming that allow a function to remember and access the variables defined in its enclosing scope, even after the enclosing function has finished executing.
Creating a Closure
To create a closure in Python, you define a nested function inside another function, and use the nested function after the enclosing function has returned. The nested function has access to the variables defined in the enclosing function, even if the enclosing function is no longer in memory.
For example:
def outer_function(x):
def inner_function():
print(x)
return inner_function
closure = outer_function(10)
closure() # Output: 10
In this example, the inner_function()
is a closure that remembers the value of x
defined in the outer_function()
. When the closure()
function is called, it prints the value of x
.
Closures can be used to create functions with different behavior, without having to define a separate function for each behavior. For example:
def make_multiplier(x):
def multiplier(y):
return x * y
return multiplier
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # Output: 10
print(triple(5)) # Output: 15
In this example, the make_multiplier()
function creates a closure that multiplies its argument by a specified value. The double()
and triple()
functions are created by calling make_multiplier()
with different values for x
.
Closures can also be used to preserve the state of a function between calls. For example:
def make_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
counter = make_counter()
print(counter()) # Output: 1
print(counter()) # Output: 2
print(counter()) # Output: 3
In this example, the make_counter()
function defines a closure that increments a counter variable each time it is called. The counter variable is preserved between calls to the counter()
function, allowing it to keep track of the number of times it has been called.
Exercises
To review these concepts, we will go through a series of exercises designed to test your understanding and apply what you have learned.
Use a closure to create a function that returns the next value in the Fibonacci sequence each time it is called.
def make_fibonacci():
a, b = 0, 1
def fibonacci():
nonlocal a, b
a, b = b, a + b
return a
return fibonacci
fib = make_fibonacci()
print(fib()) # Output: 1
print(fib()) # Output: 1
print(fib()) # Output: 2
print(fib()) # Output: 3
print(fib()) # Output: 5
Use a closure to create a function that returns the nth power of a number.
def make_power(n):
def power(x):
return x ** n
return power
square = make_power(2)
print(square(3)) # Output: 9
cube = make_power(3)
print(cube(3)) # Output: 27
quadruple = make_power(4)
print(quadruple(3)) # Output: 81
Use a closure to create a function that returns the next value in an arithmetic progression each time it is called.
def make_arithmetic_progression(start, step):
def arithmetic_progression():
nonlocal start
result = start
start += step
return result
return arithmetic_progression
progression = make_arithmetic_progression(0, 2)
print(progression()) # Output: 0
print(progression()) # Output: 2
print(progression()) # Output: 4
print(progression()) # Output: 6
Use a closure to create a function that returns the next value in a geometric progression each time it is called.
def make_geometric_progression(start, factor):
def geometric_progression():
nonlocal start
result = start
start *= factor
return result
return geometric_progression
progression = make_geometric_progression(2, 3)
print(progression()) # Output: 2
print(progression()) # Output: 6
print(progression()) # Output: 18
print(progression()) # Output: 54
Use a closure to create a function that returns the next value in a geometric progression, with a maximum value.
def make_bounded_geometric_progression(start, factor, maximum):
def bounded_geometric_progression():
nonlocal start
result = start
start *= factor
if start > maximum:
start = maximum
return result
return bounded_geometric_progression
progression = make_bounded_geometric_progression(2, 3, 50)
print(progression()) # Output: 2
print(progression()) # Output: 6
print