Python 3.12 Comprehension Inlining and PEP 709

Python 3.12 introduces comprehension inlining through PEP 709, significantly accelerating list, dictionary, and set comprehensions by eliminating internal function call overhead. Previously, comprehensions were compiled as isolated, single-use functions to protect scope integrity, introducing substantial stack allocation and variable resolution costs. PEP 709 restructures bytecode generation so that comprehensions execute inline within the parent frame while preserving variable isolation, yielding performance speedups of up to two times for comprehension execution.

The Pre-Python 3.12 Problem: Invisible Function Frames

From Python 3.0 through Python 3.11, comprehensions were compiled into distinct code objects. When the runtime encountered a comprehension, it effectively generated and called an anonymous, nested function.

This design guaranteed that internal loop variables would not leak into the enclosing scope, a fix introduced in Python 3 to resolve scope pollution from Python 2. However, this safety introduced notable performance penalties:

  1. Stack Frame Creation: Every comprehension required the runtime to allocate and push a new execution frame (PyFrameObject), initialize local variables, and tear the frame down upon completion.
  2. Variable Access Latency: When a comprehension accessed variables from the enclosing function, the compiler converted those lookups into closure operations (LOAD_DEREF or LOAD_CLOSURE) rather than fast local lookups (LOAD_FAST).
  3. Argument Marshalling: The outermost iterable had to be passed as an argument into the comprehension’s implicit function frame, adding extra stack manipulations.

For small, frequently executed comprehensions, the overhead of creating and tearing down the frame often exceeded the time spent processing the data.

How PEP 709 Solves the Overhead

PEP 709 redesigns comprehension compilation by inlining the loop directly into the containing function’s bytecode, avoiding frame instantiation entirely.

1. Scope Isolation Without Stack Frames

PEP 709 maintains variable isolation without dedicating a full function frame to the comprehension. The compiler assigns temporary registers and tracks iteration variables separately from the function's standard locals. Once the comprehension terminates, the runtime cleans up these temporary values, ensuring variables inside the comprehension never overwrite variables with the same name in the enclosing scope.

2. Conversion to Direct Bytecode Instructions

Instead of emitting a MAKE_FUNCTION instruction followed by a CALL instruction, Python 3.12 generates inline evaluation loops using new bytecode primitives. The loop runs inside the host frame, processing items and appending them directly to the target list, set, or dictionary.

3. Optimized Local Variable Access

Because the comprehension runs within the parent frame, enclosing variables are directly accessible via LOAD_FAST operations. The engine no longer requires cell indirection or closure mechanisms to read surrounding local data. This results in faster CPU register usage and lower memory latency during iteration.

Performance Gains

The performance improvements from PEP 709 vary based on the workload:

By transforming comprehensions from hidden function calls into localized bytecode loops, PEP 709 removes decades-old architectural overhead, making one of Python’s most expressive syntactic features substantially faster by default.