Skip to main content

Include/cpython/listobject.h

cpython 3.14 @ ab2d84fe1023/Include/cpython/listobject.h

CPython lists are a heap-allocated array of PyObject * pointers with over-allocation to amortise the cost of repeated appends. The PyListObject struct exposes two fields beyond the standard object header: ob_item, the pointer to the first element, and ob_alloc, the number of slots currently allocated (which can exceed ob_size). The gap between ob_size and ob_alloc is the pre-allocated headroom.

The private header is small (about 50 lines) because most list logic lives in Objects/listobject.c. What the header exposes is the concrete struct layout and a handful of _Py-prefixed helpers used by the bytecode compiler and the specialising adaptive interpreter to avoid full PyList_* call overhead in hot paths such as list comprehensions and BINARY_SUBSCR on list objects.

_PyList_ITEMS is the most frequently used macro in CPython's own C code. It casts through the concrete type and returns the raw ob_item pointer, bypassing all bounds checking. The adaptive interpreter relies on it inside type-specialised opcodes like BINARY_SUBSCR_LIST_INT.

Map

LinesSymbolRolegopy
1-20PyListObjectConcrete struct: ob_item pointer array, ob_alloc over-allocation countobjects/list.go List
21-28_PyList_ITEMS(op)Macro returning raw ob_item pointer without bounds checkobjects/list.go Items()
29-36_PyList_ExtendAppend all items from an iterable into an existing list (used by BUILD_LIST_UNPACK)objects/list.go Extend
37-43_PyList_AppendTakeRefAppend a single item and steal its reference (avoids incref/decref pair)objects/list.go AppendSteal
44-50_PyList_SET_ITEMUnchecked slot write macro used during list constructionobjects/list.go SetItemUnchecked

Reading

Struct layout (lines 1 to 20)

cpython 3.14 @ ab2d84fe1023/Include/cpython/listobject.h#L1-20

PyListObject packs three pieces of information. ob_base is the standard PyVarObject sub-object that carries ob_refcnt, ob_type, and ob_size (the logical length). ob_item is a PyObject ** pointing to the first element of the heap-allocated array. ob_alloc is a Py_ssize_t recording how many slots have been allocated, which is always at least ob_size.

The over-allocation formula in listobject.c grows by roughly 12.5% at each resize. Keeping ob_alloc in the struct lets the append fast-path check ob_size < ob_alloc without calling into the memory allocator.

typedef struct {
PyObject_VAR_HEAD
PyObject **ob_item;
Py_ssize_t ob_alloc;
} PyListObject;

Fast item access macro (lines 21 to 28)

cpython 3.14 @ ab2d84fe1023/Include/cpython/listobject.h#L21-28

_PyList_ITEMS does nothing more than expose ((PyListObject *)(op))->ob_item. Its value is that it pairs with _PyList_SET_ITEM and allows the specialising interpreter to load and store list elements without the null-check and bounds-check overhead of PyList_GET_ITEM.

Code paths that use this macro are responsible for ensuring the index is valid and the list is not being resized concurrently. In CPython this is guaranteed by the GIL; in gopy it is enforced by the single-threaded eval loop.

#define _PyList_ITEMS(op) (((PyListObject *)(op))->ob_item)

Extend and append helpers (lines 29 to 50)

cpython 3.14 @ ab2d84fe1023/Include/cpython/listobject.h#L29-50

_PyList_Extend is the internal version of list.extend. It is called by the LIST_EXTEND bytecode instruction generated for starred expressions in list literals ([*a, *b]). It handles both sequences (fast path via sq_length) and general iterables. On success it returns None; on error it returns NULL and sets the exception.

_PyList_AppendTakeRef is a reference-stealing variant of PyList_Append. The caller transfers ownership of its reference to the list, which avoids the incref in PyList_Append followed by an immediate decref in the calling code. This is used in comprehension bytecode where each element produced by the loop body is immediately consumed by the append.

PyAPI_FUNC(int) _PyList_Extend(PyListObject *, PyObject *);
PyAPI_FUNC(int) _PyList_AppendTakeRef(PyListObject *, PyObject *);

gopy mirror

objects/list.go defines the List type with an items []Object slice (Go slices already carry a separate length and capacity, which maps directly to ob_size and ob_alloc). The Items() method returns the underlying slice header, equivalent to _PyList_ITEMS. Extend and AppendSteal follow the same reference-ownership semantics as their CPython counterparts, relying on Go's garbage collector rather than manual refcounting.