Understanding Python Code Object Compiler Flags
Python code objects represent executable chunks of bytecode and store
crucial execution metadata in a bitmask attribute called
co_flags. This article breaks down what compiler flags are
and specifically examines three essential flags:
CO_OPTIMIZED, CO_NEWLOCALS, and
CO_VARKEYWORDS. You will learn how these flags inform the
CPython interpreter about a code block's scoping rules, argument
handling, and internal variable optimizations.
What Are Code Object Flags?
When Python compiles source code into bytecode, it creates a
types.CodeType object containing instructions, constants,
variable names, and execution metadata. The co_flags
attribute on a code object is an integer representing a bitwise mask of
compilation flags. These flags tell the CPython virtual machine how to
construct stack frames, allocate namespaces, and bind arguments at
runtime.
You can inspect a function's flags directly using the
inspect module or by querying
func.__code__.co_flags.
CO_OPTIMIZED (Value:
0x0001)
The CO_OPTIMIZED flag indicates that the code object
uses optimized local variable access.
In module-level code or class definitions, local variables are
dynamically stored and accessed via a dictionary namespace. In contrast,
standard functions have a fixed set of local variables known at compile
time. When CO_OPTIMIZED is set:
- The interpreter allocates an array of fixed size for local variables directly on the execution frame.
- The bytecode uses fast indexed access instructions, namely
LOAD_FASTandSTORE_FAST, rather than dictionary lookups viaLOAD_NAMEorSTORE_NAME. - Variable resolution avoids hash table lookups, significantly improving function execution speed.
CO_NEWLOCALS (Value:
0x0002)
The CO_NEWLOCALS flag signals to the Python virtual
machine that a new local namespace dictionary must be constructed when
the code block executes.
- When present (typically in functions), calling the code creates a brand-new, isolated local scope. This prevents local variables from leaking into parent scopes or persisting across separate function calls.
- When absent (such as in module-level scripts or
exec()blocks without custom namespaces), the execution frame reuses an existing namespace—frequently the global dictionary—as its locals.
In standard function definitions, CO_OPTIMIZED and
CO_NEWLOCALS are almost always enabled together.
CO_VARKEYWORDS (Value:
0x0008)
The CO_VARKEYWORDS flag denotes that a function accepts
arbitrary keyword arguments, defined in the source code using the
double-asterisk syntax (e.g., **kwargs).
When the interpreter invokes a function:
- It checks
CO_VARKEYWORDSduring argument parsing and tuple unpacking. - If the flag is set, any keyword arguments provided by the caller that do not match positional or keyword-only parameters are gathered into a new dictionary.
- This dictionary is assigned to the designated
**kwargsparameter slot in the frame's local array before bytecode execution begins. - If extra keyword arguments are passed to a function that lacks this
flag, Python raises a
TypeError.
Checking Flags in Python
CPython exposes these flag definitions in the standard library's
dis module. You can check for their presence using bitwise
operations:
import dis
def example_function(a, **kwargs):
b = 10
return a + b
flags = example_function.__code__.co_flags
print(bool(flags & dis.COMPILER_FLAG_NAMES[1])) # CO_OPTIMIZED
print(bool(flags & dis.COMPILER_FLAG_NAMES[2])) # CO_NEWLOCALS
print(bool(flags & dis.COMPILER_FLAG_NAMES[8])) # CO_VARKEYWORDSUnderstanding these flags provides insight into how CPython distinguishes standard functions from dynamic scopes and manages runtime performance behind the scenes.