Role of CO_VARARGS in Python Bytecode Execution
The CO_VARARGS flag is a compiler-level bit flag stored
in a Python code object’s co_flags attribute that signifies
a function accepts arbitrary positional arguments via the
*args syntax. During bytecode execution, this flag
instructs Python's runtime evaluation loop and argument-binding routines
to collect any positional arguments that exceed the explicitly declared
positional parameters, package them into a single tuple, and bind that
tuple to a dedicated local variable slot in the execution frame.
Flag Definition and Compilation
When Python compiles source code into a code object
(PyCodeObject), the compiler analyzes the function
signature. If the signature includes a variable positional parameter
(such as *args), the compiler sets the
CO_VARARGS bit (internally defined as 0x0004
in CPython’s code.h) within co_flags.
You can inspect this flag directly using the inspect
module:
import inspect
def example_func(a, *args):
pass
# CO_VARARGS corresponds to inspect.CO_VARARGS (value 4)
has_varargs = bool(example_func.__code__.co_flags & inspect.CO_VARARGS)Argument Binding During Frame Initialization
Before the CPython virtual machine begins executing the bytecode
instructions of a function frame, it must resolve incoming arguments.
This is handled by internal evaluation functions (such as
_PyEval_Vector or _PyFunction_Vectorcall).
The execution sequence proceeds as follows:
- Positional Parameter Mapping: The interpreter first maps supplied positional arguments to the function's explicit positional parameter slots.
- Excess Argument Detection: If the caller provides
more positional arguments than the number of positional parameters
defined by
co_argcount, the interpreter checksco_flags & CO_VARARGS. - Tuple Allocation:
- If
CO_VARARGSis set, the interpreter creates a newPyTupleobject containing the remaining positional arguments. - If
CO_VARARGSis not set, the interpreter raises aTypeErrorindicating that the function was given more positional arguments than it accepts.
- If
- Empty Fallback: If the function defines
*argsbut no excess positional arguments were supplied, the runtime assigns an empty tuple()to the argument slot.
Local Variable Assignment and Bytecode Access
Once the tuple is constructed, the runtime places the reference to
this tuple directly into the execution frame's array of local variables
(fastlocals). The designated index in this array is
immediately after the standard positional and keyword-only parameter
slots.
Inside the function body, bytecode does not require a special
instruction to read *args. The compiler emits a standard
LOAD_FAST instruction referencing the local variable slot
assigned to args. Because the packing process occurs
entirely during the C-level function call preamble, the bytecode
execution loop treats the variable positional argument container as an
ordinary local tuple.