Skip to main content

Objects/iterobject.c

cpython 3.14 @ ab2d84fe1023/Objects/iterobject.c

Two concrete iterator types live here. PySeqIter_Type wraps any object whose type implements sq_item (the sequence-item slot): it stores the object and a monotonically increasing index and calls PySequence_GetItem(seq, index++) on each next(). When GetItem raises IndexError the iterator treats that as exhaustion and clears the error.

PyCallIter_Type wraps a zero-argument callable and a sentinel: on each next() it calls the callable and compares the result to the sentinel via PyObject_RichCompareBool(..., Py_EQ); equality stops iteration. This is the mechanism behind iter(callable, sentinel) (the two-argument form of the built-in iter). Both types implement __length_hint__ so that operator.length_hint() and pre-allocation hints in list() work correctly.

Map

LinesSymbolRolegopy
1-50PySeqIter_New, iter_dealloc, iter_traverseAllocate a sequence iterator, release it, GC traversal.objects/iter.go:NewSeqIter
51-120iter_iternext, iter_lenAdvance the sequence iterator by calling PySequence_GetItem; __length_hint__ via sq_length.objects/iter.go:(*SeqIter).Next
121-160PySeqIter_TypeType object for sequence iterators: tp_iter returns self, tp_iternext = iter_iternext.objects/iter.go:SeqIterType
161-200PyCallIter_New, calliter_dealloc, calliter_traverseAllocate a callable iterator storing callable + sentinel; release and GC traversal.objects/iter.go:NewCallIter
201-250calliter_iternext, PyCallIter_TypeCall it_callable(), compare result to sentinel; type object definition.objects/iter.go:(*CallIter).Next

Reading

Sequence iterator (lines 1 to 160)

cpython 3.14 @ ab2d84fe1023/Objects/iterobject.c#L1-160

PySeqIter_New allocates a seqiterobject, takes a reference to the sequence, and sets it_index = 0. iter_iternext is the workhorse:

static PyObject *
iter_iternext(PyObject *iterator)
{
seqiterobject *it = (seqiterobject *)iterator;
PyObject *seq = it->it_seq;
PyObject *result;

if (seq == NULL)
return NULL;

result = PySequence_GetItem(seq, it->it_index);
if (result != NULL) {
++it->it_index;
return result;
}
if (PyErr_ExceptionMatches(PyExc_StopIteration)
|| PyErr_ExceptionMatches(PyExc_IndexError))
{
PyErr_Clear();
Py_CLEAR(it->it_seq);
}
return NULL;
}

Two exception types signal exhaustion: IndexError (the historical sequence protocol) and StopIteration (for objects that have already been adapted to raise StopIteration directly). After exhaustion, it_seq is cleared to release the reference and to mark the iterator as finished, so subsequent calls return NULL immediately without calling PySequence_GetItem again.

iter_len implements __length_hint__: it calls PySequence_Size on the underlying sequence and subtracts it_index. If PySequence_Size returns -1 (the sequence does not know its length), iter_len returns PY_ITERSIZEUNKNOWN (-1), which operator.length_hint treats as an unknown hint.

Callable iterator (lines 161 to 250)

cpython 3.14 @ ab2d84fe1023/Objects/iterobject.c#L161-250

PyCallIter_New stores the callable and sentinel and performs no validation beyond type-checking that callable is actually callable:

PyObject *
PyCallIter_New(PyObject *callable, PyObject *sentinel)
{
calliterobject *it;
it = PyObject_GC_New(calliterobject, &PyCallIter_Type);
if (it == NULL)
return NULL;
it->it_callable = Py_NewRef(callable);
it->it_sentinel = Py_NewRef(sentinel);
_PyObject_GC_TRACK(it);
return (PyObject *)it;
}

calliter_iternext calls it->it_callable with no arguments via PyObject_CallNoArgs, then compares the result to it->it_sentinel using PyObject_RichCompareBool(..., Py_EQ). If they compare equal, the sentinel is cleared, the callable is cleared, and NULL is returned (exhaustion). If the callable raises any exception it propagates as-is; the caller of iter() is expected to arrange for the sentinel to be raised on exhaustion, not an exception.

gopy mirror

objects/iter.go. seqiterobject maps to SeqIter with fields ItSeq *Object and ItIndex int. calliterobject maps to CallIter with fields ItCallable *Object and ItSentinel *Object. Both (*SeqIter).Next and (*CallIter).Next return (Object, error) with nil, nil signalling exhaustion (matching CPython's NULL + no-exception convention). __length_hint__ is the LengthHint() int method on SeqIter.

CPython 3.14 changes

Both iterator types are stable and unchanged since Python 3.0. The __length_hint__ method (iter_len) was added in 3.4 (PEP 424). The only 3.14-era change is use of Py_NewRef and Py_CLEAR macros in place of the older Py_INCREF/Py_XDECREF style.