Skip to main content

Modules/_heapqmodule.c (part 2)

Source:

cpython 3.14 @ ab2d84fe1023/Modules/_heapqmodule.c

This annotation covers the combined push/pop operations and the nlargest/nsmallest helpers. See modules_heapq1_detail for heappush, heappop, and heapify.

Map

LinesSymbolRole
1-80heappushpopPush then pop in one operation (faster than two calls)
81-160heapreplacePop root then push (no size change)
161-280_siftup / _siftdownInternal heap fixup helpers
281-400nlargestFind N largest elements efficiently
401-500nsmallestFind N smallest elements efficiently

Reading

heappushpop

// CPython: Modules/_heapqmodule.c:280 heappushpop
static PyObject *
heappushpop(PyObject *self, PyObject *args)
{
/* If item <= heap[0], return item directly without touching the heap. */
PyObject *heap, *item;
if (!PyArg_UnpackTuple(args, "heappushpop", 2, 2, &heap, &item))
return NULL;
if (PyList_GET_SIZE(heap) == 0) return item;
PyObject *top = PyList_GET_ITEM(heap, 0);
int cmp = PyObject_RichCompareBool(item, top, Py_LT);
if (cmp < 0) return NULL;
if (cmp) {
Py_INCREF(item);
return item; /* item is smaller than root; heap unchanged */
}
/* item >= root: replace root, sift down */
PyObject *returnval = Py_NewRef(top);
PyList_SET_ITEM(heap, 0, Py_NewRef(item));
_siftup((PyListObject *)heap, 0);
return returnval;
}

heappushpop is faster than heappush followed by heappop because the "item is smaller" case is O(1). Used in nlargest to maintain a running min-heap of the N largest seen so far.

heapreplace

// CPython: Modules/_heapqmodule.c:340 heapreplace
static PyObject *
heapreplace(PyObject *self, PyObject *args)
{
/* Pop root and push new item; heap size stays constant. */
PyObject *heap, *item;
PyObject *returnval = Py_NewRef(PyList_GET_ITEM(heap, 0));
PyList_SET_ITEM(heap, 0, Py_NewRef(item));
_siftup((PyListObject *)heap, 0);
return returnval;
}

heapreplace is O(log n). Unlike heappop + heappush it does one sift instead of two. Raises IndexError on an empty heap. The new item may be larger or smaller than the popped root.

nlargest

// CPython: Modules/_heapqmodule.c:420 nlargest
static PyObject *
nlargest(PyObject *self, PyObject *args)
{
/* Build a min-heap of size n; replace root when a larger item is seen. */
Py_ssize_t n;
PyObject *it = PyObject_GetIter(iterable);
/* Fill heap with first n items */
...
/* For remaining items: if item > heap[0], heapreplace */
while ((item = PyIter_Next(it)) != NULL) {
if (PyObject_RichCompareBool(item, PyList_GET_ITEM(heap, 0), Py_GT) > 0)
heapreplace_internal(heap, item);
else
Py_DECREF(item);
}
/* Sort descending */
PyList_Sort(heap);
PyList_Reverse(heap);
return heap;
}

nlargest(n, iterable) uses a min-heap of size n: the root is the smallest of the n largest seen. For n << len(iterable) this is O(m log n) vs O(m log m) for full sort. For small n or small m, it falls back to sorted(iterable, reverse=True)[:n].

gopy notes

heappushpop is module/heapq.HeapPushPop in module/heapq/module.go. heapreplace is module/heapq.HeapReplace. nlargest and nsmallest use objects.ListSort and objects.ListReverse for the final sort. _siftup and _siftdown are unexported helpers siftUp / siftDown.