Python Closures: How to Retain State in Functions and the nonlocal Keyword

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:

  1. Nested Function: Define another function (inner function) inside a function.
  2. Reference to External Variables: The inner function refers to variables of the outer function.
  3. 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.

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次