Skip to main content

Objects/listobject.c

Source:

cpython 3.14 @ ab2d84fe1023/Objects/listobject.c

Objects/listobject.c implements the list type. Lists are dynamic arrays of PyObject* pointers with amortized O(1) append. The file provides list_append, list_insert, list_sort (timsort), list_reverse, and the full sequence protocol.

Map

LinesSymbolRole
1-100free listPer-interpreter free list for empty list objects
101-250PyList_New, list_deallocAllocator and deallocator
251-450list_resize, list_append, list_insertDynamic growth and mutation
451-700list_sortTimsort entry point; merge_* helpers
701-1200Timsort merge logicmerge_lo, merge_hi, galloping
1201-1700list_reverse, list_extend, list_popOther mutation methods
1701-2500Sequence protocol, type wiringsq_item, sq_slice, PyList_Type

Reading

Dynamic growth

list_resize(self, newsize) reallocates ob_item when the size changes. The growth formula over-allocates: new_allocated = newsize + newsize // 8 + (3 if newsize < 9 else 6). This amortizes reallocation to O(1) per append.

// Objects/listobject.c:251 list_resize
static int
list_resize(PyListObject *self, Py_ssize_t newsize)
{
Py_ssize_t new_allocated;
if (newsize <= self->allocated && newsize >= self->allocated / 2) {
Py_SET_SIZE(self, newsize);
return 0;
}
new_allocated = ((size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6));
...
self->ob_item = (PyObject **)PyMem_Realloc(self->ob_item,
new_allocated * sizeof(PyObject *));
self->allocated = new_allocated;
return 0;
}

Timsort

list.sort() uses timsort, an adaptive merge sort that exploits existing runs of sorted data. The key steps: count_run finds the length of each natural ascending or descending run; binarysort sorts short runs; merge_collapse merges adjacent runs on the pending-run stack; galloping mode accelerates merges when one run consistently has smaller elements.

// Objects/listobject.c:451 list_sort_impl
static PyObject *
list_sort_impl(PyListObject *self, PyObject *keyfunc, int reverse)
{
MergeState ms;
merge_init(&ms, nelem, has_keyfunc, &lo);
while (remaining > 1) {
n = count_run(&ms, lo.keys, lo.keys + remaining, &descending);
push_next_run(&ms, n, ...);
merge_collapse(&ms);
lo.keys += n; remaining -= n;
}
merge_force_collapse(&ms);
...
}

Free list

Empty list objects (ob_alloc == 0) are cached on a per-interpreter free list of up to PyList_MAXFREELIST (80) items. list_dealloc pushes to the free list; PyList_New(0) pops from it.

gopy notes

The gopy list is implemented using Go's []py.Object slice with the same amortized growth factor. timsort is available via Go's sort.Slice (which uses pdqsort in recent versions); a faithful timsort port lives in vm/ or objects/.