Codectionary / Developer documentation / Python

Closures

A closure is a function that remembers and has access to variables from its enclosing scope, even after that outer function has finished executing. This happens when an inner function is defined inside an outer function and references the outer function's variables, then gets returned or passed elsewhere. Closures are the mechanism behind function factories and are commonly used to create functions with private, persistent state.

Syntax

def outer():\n    x = value\n    def inner():\n        return x\n    return inner

Examples

Basic Closure

An inner function that 'remembers' a variable from its enclosing function.

def make_greeter(greeting):
    def greet(name):
        return f"{greeting}, {name}!"
    return greet

hello_greeter = make_greeter("Hello")
hey_greeter = make_greeter("Hey")

print(hello_greeter("John"))  # Hello, John!
print(hey_greeter("Zain"))    # Hey, Zain!

Closure with Persistent State (Counter)

Using a closure to maintain private state between calls, without using a class.

def make_counter():
    count = 0
    def increment():
        nonlocal count  # allows modifying the outer variable
        count += 1
        return count
    return increment

counter = make_counter()
print(counter())  # 1
print(counter())  # 2
print(counter())  # 3

another_counter = make_counter()
print(another_counter())  # 1 (completely independent state)

Practical Example: Configurable Validators

Using a closure to build reusable, pre-configured validation functions.

def make_range_validator(minimum, maximum):
    def validate(value):
        return minimum <= value <= maximum
    return validate

is_valid_age = make_range_validator(0, 120)
is_valid_percentage = make_range_validator(0, 100)

print(is_valid_age(25))         # True
print(is_valid_percentage(150)) # False

Best practices

  • Use the 'nonlocal' keyword when an inner function needs to modify (not just read) a variable from its enclosing scope
  • Use closures for lightweight cases of remembered state (a counter, a configured function) instead of writing a full class when it fits
  • Each call to the outer function creates a fresh, independent closure - variables are not shared between separately created closures
  • Prefer a class with __call__ over a closure once the amount of internal state or behavior grows beyond one or two variables

Runtime & versions

Python interpreter. Feature availability depends on the language and runtime version used by your project; consult the official reference below.

Python language reference

Specifications & further reading