22.6 Closures vs Classes: When to Use Which

Closures and classes represent two distinct but often overlapping paradigms for managing state and behavior in Python. The choice between them hinges on the nature of the state, the required complexity, and the intended use of the construct. A closure is a function object that remembers values in enclosing scopes even if they are not present in memory. It is created by nesting a function inside another function and returning the inner function, which has captured variables from the outer function’s scope. A class, on the other hand, is a blueprint for creating objects that encapsulate both data (attributes) and behavior (methods), offering a more structured and explicit approach to state management.

22.5 Practical Closure Patterns: Factories and Counters

The Factory Pattern with Closures A factory function is a function that returns another function, often customizing the returned function’s behavior based on the arguments passed to the factory. Closures are the fundamental mechanism that makes this pattern so powerful and memory-efficient in Python. The inner function “closes over” the variables from the factory’s scope, preserving them for the lifetime of the returned function object. This allows each returned function to maintain its own unique state without relying on global variables or class-based constructs.

22.4 nonlocal: Modifying Enclosing Variables

The nonlocal keyword is a crucial tool for managing state within nested functions, enabling inner functions to modify variables from their enclosing (non-global) scope. It was introduced in Python 3 to resolve the ambiguity and limitations of using mutable objects for state changes and to provide a cleaner alternative to the global keyword for nested scopes. The Problem nonlocal Solves Without nonlocal, an inner function can read variables from an enclosing scope, but any assignment to a name will create a new local variable within that inner function’s scope, shadowing the name from the outer scope. This often leads to unexpected UnboundLocalError exceptions.

22.3 The Late-Binding Closure Gotcha

One of the most common and perplexing issues encountered by Python programmers when first working with closures is the late-binding behavior of closures. This behavior often leads to unexpected results, especially when the closure is created inside a loop. Understanding this mechanism is crucial for writing correct and predictable code. The Problem: Unexpected Closure Values Consider a scenario where you want to create a list of simple functions, each of which returns a number when called. You might try to create them in a loop.

22.2 Free Variables and __closure__

In Python, a free variable is a variable that is used in a function’s code block but is not defined there and is not a global variable. Instead, it is “captured” or “bound” from an enclosing function’s scope. This mechanism is the very foundation of a closure, a function object that remembers values in enclosing scopes even if those scopes are no longer present in memory. The Anatomy of a Closure A closure is created when a nested function references a value from its enclosing scope, and that nested function is returned to a context where the enclosing scope has ceased to exist. The closure “closes over” the free variable, preserving it for later use.

22.1 What Is a Closure?

A closure is a function object that retains access to variables from the scope in which it was created, even after that scope has finished executing. This powerful concept enables functions to “remember” and manipulate their lexical environment long after the outer function has returned, effectively creating stateful functions without relying on classes or global variables. The mechanism behind closures relies on Python’s scoping rules and how it handles variable references. When a function is defined, it captures (or “closes over”) references to the non-local variables it needs, preserving them in a special attribute called __closure__. This tuple of cell objects contains the actual values from the enclosing scope, allowing the inner function to access them even when the outer function is no longer active.

— joke —

...