In Python, a “Closure” is an important concept in functional programming. Simply put, it refers to “a function that continues to hold variables from the environment (scope) in which it was defined.”
Normally, local variables defined within a function are discarded from memory when the function finishes executing. However, by using a closure, you can retain (remember) the values of variables inside a function even after it has finished executing. This is very effective when you want to create a mechanism that has “state” without using classes.
This article explains the basic mechanism of closures and the nonlocal statement required to update variables.
Basic Structure of a Closure
To create a closure, the following conditions must be met:
- Nested Function: Define another function (inner function) inside a function.
- Reference to External Variables: The inner function refers to variables of the outer function.
- Return the Function: The outer function returns the “inner function itself” as a return value.
Specific Code Example: Creating a Multiplier
As an example, let’s create a “factory function” that generates a function to multiply numbers by a specified factor.
def create_multiplier(factor):
"""
Returns a closure that retains the specified multiplier (factor).
"""
# --- Inner Function ---
def multiplier(number):
# Refers to the variable 'factor' of the outer function
return number * factor
# ----------------------
# Return the inner function object without executing it
return multiplier
# 1. Create a function that "multiplies by 3"
times_three = create_multiplier(3)
# 2. Create a function that "multiplies by 5"
times_five = create_multiplier(5)
# Execute the generated functions
print(f"10 times 3: {times_three(10)}")
print(f"10 times 5: {times_five(10)}")
# Check the environment held by the function (Reference)
print(f"Held value: {times_three.__closure__[0].cell_contents}")
Output:
10 times 3: 30
10 times 5: 50
Held value: 3
Explanation
When the execution of create_multiplier(3) finishes, the argument factor should normally be destroyed. However, the returned times_three function (which is actually multiplier) continues to retain the state of factor=3, which was the environment when it was defined. This is a closure.
Updating State: The nonlocal Declaration
Care must be taken when you want to not only “reference” but also “update (rewrite)” the variable held by the closure.
In Python, if you assign to a variable inside a function, it is automatically considered a “local variable within that function.” Therefore, if you try to rewrite an outer variable from an inner function, a new variable is created, and it will not work as intended (or will result in an error).
To explicitly update an outer variable, use the nonlocal declaration.
Specific Code Example: Wallet (Updating State)
Let’s create a function like a “piggy bank” that adds to the total amount every time it is called.
def create_wallet(initial_balance):
"""
Returns a closure that retains the balance and updates it upon deposit.
"""
balance = initial_balance
def deposit(amount):
# Declare that we are updating the outer variable 'balance'
nonlocal balance
balance += amount
print(f"Deposited {amount} yen. Balance: {balance} yen")
return balance
return deposit
# Create a wallet with 1000 yen
my_wallet = create_wallet(1000)
# The state of 'balance' is updated and retained each time the function is called
my_wallet(500)
my_wallet(200)
my_wallet(1000)
Output:
Deposited 500 yen. Balance: 1500 yen
Deposited 200 yen. Balance: 1700 yen
Deposited 1000 yen. Balance: 2700 yen
Without nonlocal
If you do not write nonlocal balance in the code above, an UnboundLocalError (local variable referenced before assignment) will occur at the line balance += amount. This is because Python interprets balance as a new local variable inside the deposit function, but tries to add to it before it is initialized.
Summary
- Closure: An inner function that can carry its environment around, remembering variables from the outer function.
- Creation: Define a function inside a function and return that inner function.
- Retaining State: You can handle data (state) and processing (functions) as a set without using classes.
nonlocal: A mandatory declaration when “changing” the value of a variable held within a closure.
Closures are the foundation for advanced Python features such as Decorators and state management in callback functions.
