Fundamentals 18 min read

How Does Python Implement Closures? A Deep Dive into Bytecode and localsplus

This article explains the inner workings of Python closures by tracing how nested functions are compiled into bytecode, how the CPython VM creates cell objects, binds them via the func_closure field, and stores variables in the localsplus array for static access.

Satori Komeiji's Programming Classroom
Satori Komeiji's Programming Classroom
Satori Komeiji's Programming Classroom
How Does Python Implement Closures? A Deep Dive into Bytecode and localsplus

Python closures are created when an inner function references variables from its outer function. The outer function’s local variables that are captured become cell variables , stored as PyCellObject instances, and the inner function receives a tuple of these cells in its func_closure field.

Two illustrative examples show how a simple login function can be wrapped by a decorator to freeze the username and password, and how a nested deco function returns the inner login as a closure.

The CPython implementation relies on several PyCodeObject fields: co_localsplusnames: names of locals, cell variables, and free variables co_nlocalsplus: total count of those names co_varnames: only locals co_nlocals: count of locals co_cellvars and co_ncellvars: names and count of cell variables co_freevars and co_nfreevars: names and count of free variables

All these names are stored in the stack‑frame’s localsplus array, which is divided into four regions: locals, cell variables, free variables, and the runtime stack.

During compilation, the VM emits specific bytecode instructions: MAKE_CELL creates a PyCellObject for each captured variable (e.g., name and age). STORE_DEREF stores a value into a cell’s ob_ref field, turning a normal local assignment into a cell assignment. LOAD_CLOSURE pushes a cell object onto the stack so it can be packaged into the closure tuple. MAKE_FUNCTION builds the inner function object and attaches the closure tuple to func_closure. COPY_FREE_VARS copies the closure’s cells into the inner function’s free‑variable region of localsplus. LOAD_DEREF reads the ob_ref from a cell when the inner function accesses a captured variable.

When the outer function finishes, its locals remain alive inside the cells, allowing the inner function to read them later. The article shows the exact bytecode for a sample some_func and its inner inner, highlighting how indices in the bytecode correspond to positions in localsplus.

Finally, the article demonstrates calling the closure, inspecting func.__closure__, and printing the cell contents, confirming that the captured values ( 17 and "satori") are still accessible.

In summary, a Python closure is still a regular function object whose func_closure field holds references to cell objects; the VM’s static array layout and compile‑time indices guarantee deterministic, fast access to captured variables.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonbytecodeclosurenamespacefunctionCPythonlocalspluspycellobject
Satori Komeiji's Programming Classroom
Written by

Satori Komeiji's Programming Classroom

Python and Rust developer; I write about any topics you're interested in. Follow me! (#^.^#)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.