Source: Jp Valery on Unsplash
In Python, a closure is characterized by the following:
In essence, a closure is an inner function that "closes over" and remembers the variables from its surrounding environment, even when the outer function is no longer running.
def outer_function(msg):
message = msg
def inner_function():
print(message)
return inner_function
hi_func = outer_function("Hi")
hello_func = outer_function("Hello")
hi_func() # Output: Hi
hello_func() # Output: Hello
Explanation:
outer_function
takes an argument msg
and defines a local variable message
with this value.outer_function
, another function inner_function
is defined. This inner_function
accesses the message
variable from the enclosing scope of outer_function
. The message
variable is a "free variable" within inner_function
because it's not defined within inner_function
itself.outer_function
returns the inner_function
.outer_function("Hi")
, it returns the inner_function
with the message
variable "closed over" the value "Hi". This returned function is assigned to hi_func
.hello_func
becomes the inner_function
with "message" closed over "Hello".outer_function
has finished executing, the returned inner_function
(now referenced by hi_func
and hello_func
) still retains access to the message
variable from its creation environment. When we call hi_func()
and hello_func()
, they print the respective "closed over" messages.Closures are often used to create function factories, where you generate functions with pre-configured behavior.
def exponent_function(n):
def raise_to_power(x):
return x ** n
return raise_to_power
square = exponent_function(2) # Create a function to square numbers
cube = exponent_function(3) # Create a function to cube numbers
print(square(5)) # Output: 25
print(cube(5)) # Output: 125
Closures are a fundamental part of Python decorators, which provide a way to modify or enhance the behavior of functions or methods.
def my_decorator(func):
def wrapper_function(*args, **kwargs):
print("Before calling the function.")
result = func(*args, **kwargs)
print("After calling the function.")
return result
return wrapper_function
@my_decorator
def say_hello(name):
return f"Hello, {name}!"
print(say_hello("Alice"))
# Output:
# Before calling the function.
# After calling the function.
# Hello, Alice!
Closures can be used to maintain state across multiple calls to a function.
def counter():
count = 0
def increment():
nonlocal count # Allows modification of 'count' in the enclosing scope
count += 1
return count
return increment
my_counter = counter()
print(my_counter()) # Output: 1
print(my_counter()) # Output: 2
print(my_counter()) # Output: 3