Skip to main content

Modules/_collectionsmodule.c

cpython 3.14 @ ab2d84fe1023/Modules/_collectionsmodule.c

_collectionsmodule.c provides the C-level implementations that back collections.deque and the _tuplegetter descriptor used internally by collections.namedtuple. The higher-level ChainMap, Counter, OrderedDict, and defaultdict types live elsewhere (Objects/odictobject.c, Modules/_collectionsmodule.c for defaultdict), but deque is by far the largest component and the reason this file exists as a C accelerator rather than pure Python.

deque is implemented as a doubly-linked list of fixed-size blocks (BLOCKLEN = 64 items per block). This layout gives O(1) appends and pops at both ends without per-item allocation overhead, and keeps the memory footprint small for short deques. A maxlen constraint is enforced at every append: when the deque is full, the element at the opposite end is silently discarded rather than raising an error.

_tuplegetter is a lightweight descriptor that stores an integer index and an optional docstring. namedtuple creates one per field and installs it on the generated class. Accessing point.x then dispatches through tuplegetter_descr_get, which calls PyTuple_GET_ITEM(obj, index) directly, avoiding the usual attribute lookup chain.

Map

LinesSymbolRolegopy
1-80block struct + BLOCKLENFixed-size storage node for the doubly-linked block list
81-350dequeobject struct + deque_newDeque header: left/right block, left/right index, length, maxlen, state
351-700deque_append / deque_appendleftAmortised O(1) push with optional maxlen eviction
701-950deque_pop / deque_popleftO(1) pop with empty-check and block recycling
951-1200deque_rotateIn-place rotation using repeated block pointer swaps
1201-1550deque_count, deque_index, deque_containsLinear scan helpers not present on list
1551-1750dequeiter / dequereviterForward and reverse iterator objects with invalidation guard
1751-2000tuplegetter descriptor + module initnamedtuple field accessor and PyModuleDef

Reading

Block-list memory model

Each block holds exactly BLOCKLEN (64) PyObject * slots. The deque tracks leftblock, leftindex, rightblock, and rightindex. A fresh append to the right side increments rightindex; when it reaches BLOCKLEN a new block is allocated and linked. Pops decrement the index; when a block is emptied it is freed (or recycled into a one-block free list). This design means sequential appends never touch the allocator after the first block fills.

maxlen eviction

deque_append checks Py_SIZE(deque) == deque->maxlen before inserting. If the deque is full it calls deque_popleft (for append) or deque_pop (for appendleft) to drop the oldest element, then proceeds with the insert. The net result is that len(d) never exceeds maxlen and no exception is raised.

rotate implementation

deque_rotate(n) moves abs(n) elements from one end to the other by calling append/pop pairs. For large rotations it short-circuits by relinking whole blocks at the pointer level rather than moving items one at a time, keeping the operation O(min(n, len)) rather than O(n).

Iterator invalidation

Both dequeiter and dequereviter store a copy of deque->state at creation time. Every mutating method on deque increments state. If state has changed when __next__ is called, the iterator raises RuntimeError: deque changed size during iteration, mirroring list iterator behavior.

_tuplegetter descriptor

tuplegetter_descr_get(self, obj, type) checks that obj is a tuple and calls PyTuple_GET_ITEM(obj, self->index). The descriptor also implements __set_name__ to adopt the field name as its __doc__ prefix when the namedtuple class is built, so help(Point.x) shows something useful.

/* _collectionsmodule.c: append fast path (abbreviated) */
static int
deque_append_internal(dequeobject *deque, PyObject *item, Py_ssize_t maxlen)
{
if (deque->rightindex == BLOCKLEN - 1) {
block *b = newblock();
if (b == NULL) return -1;
b->leftlink = deque->rightblock;
deque->rightblock->rightlink = b;
deque->rightblock = b;
deque->rightindex = -1;
}
Py_SIZE(deque)++;
deque->rightindex++;
deque->rightblock->data[deque->rightindex] = item;
...
}

gopy mirror

Not yet ported.