Skip to content

Commit 213c7a6

Browse files
committed
Mondo changes to the iterator stuff, without changing how Python code
sees it (test_iter.py is unchanged). - Added a tp_iternext slot, which calls the iterator's next() method; this is much faster for built-in iterators over built-in types such as lists and dicts, speeding up pybench's ForLoop with about 25% compared to Python 2.1. (Now there's a good argument for iterators. ;-) - Renamed the built-in sequence iterator SeqIter, affecting the C API functions for it. (This frees up the PyIter prefix for generic iterator operations.) - Added PyIter_Check(obj), which checks that obj's type has a tp_iternext slot and that the proper feature flag is set. - Added PyIter_Next(obj) which calls the tp_iternext slot. It has a somewhat complex return condition due to the need for speed: when it returns NULL, it may not have set an exception condition, meaning the iterator is exhausted; when the exception StopIteration is set (or a derived exception class), it means the same thing; any other exception means some other error occurred.
1 parent 8b3d6ca commit 213c7a6

9 files changed

Lines changed: 182 additions & 58 deletions

File tree

Include/abstract.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,11 +470,24 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
470470
471471
*/
472472

473+
/* Iterators */
474+
473475
DL_IMPORT(PyObject *) PyObject_GetIter(PyObject *);
474476
/* Takes an object and returns an iterator for it.
475477
This is typically a new iterator but if the argument
476478
is an iterator, this returns itself. */
477479

480+
#define PyIter_Check(obj) \
481+
(PyType_HasFeature((obj)->ob_type, Py_TPFLAGS_HAVE_ITER) && \
482+
(obj)->ob_type->tp_iternext != NULL)
483+
484+
DL_IMPORT(PyObject *) PyIter_Next(PyObject *);
485+
/* Takes an iterator object and calls its tp_iternext slot,
486+
returning the next value. If the iterator is exhausted,
487+
this can return NULL without setting an exception, *or*
488+
NULL with a StopIteration exception.
489+
NULL with any other exception means an error occurred. */
490+
478491
/* Number Protocol:*/
479492

480493
DL_IMPORT(int) PyNumber_Check(PyObject *o);

Include/iterobject.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
/* Iterators (the basic kind, over a sequence) */
22

3-
extern DL_IMPORT(PyTypeObject) PyIter_Type;
3+
extern DL_IMPORT(PyTypeObject) PySeqIter_Type;
44

5-
#define PyIter_Check(op) ((op)->ob_type == &PyIter_Type)
5+
#define PySeqIter_Check(op) ((op)->ob_type == &PySeqIter_Type)
66

7-
extern DL_IMPORT(PyObject *) PyIter_New(PyObject *);
7+
extern DL_IMPORT(PyObject *) PySeqIter_New(PyObject *);
88

99
extern DL_IMPORT(PyTypeObject) PyCallIter_Type;
1010

Include/object.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ typedef PyObject *(*reprfunc)(PyObject *);
201201
typedef long (*hashfunc)(PyObject *);
202202
typedef PyObject *(*richcmpfunc) (PyObject *, PyObject *, int);
203203
typedef PyObject *(*getiterfunc) (PyObject *);
204+
typedef PyObject *(*iternextfunc) (PyObject *);
204205

205206
typedef struct _typeobject {
206207
PyObject_VAR_HEAD
@@ -252,6 +253,7 @@ typedef struct _typeobject {
252253

253254
/* Iterators */
254255
getiterfunc tp_iter;
256+
iternextfunc tp_iternext;
255257

256258
#ifdef COUNT_ALLOCS
257259
/* these must be last and never explicitly initialized */

Objects/abstract.c

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1748,10 +1748,32 @@ PyObject_GetIter(PyObject *o)
17481748
f = t->tp_iter;
17491749
if (f == NULL) {
17501750
if (PySequence_Check(o))
1751-
return PyIter_New(o);
1751+
return PySeqIter_New(o);
17521752
PyErr_SetString(PyExc_TypeError, "iter() of non-sequence");
17531753
return NULL;
17541754
}
1755-
else
1756-
return (*f)(o);
1755+
else {
1756+
PyObject *res = (*f)(o);
1757+
if (res != NULL && !PyIter_Check(res)) {
1758+
PyErr_Format(PyExc_TypeError,
1759+
"iter() returned non-iterator "
1760+
"of type '%.100s'",
1761+
res->ob_type->tp_name);
1762+
Py_DECREF(res);
1763+
res = NULL;
1764+
}
1765+
return res;
1766+
}
1767+
}
1768+
1769+
PyObject *
1770+
PyIter_Next(PyObject *iter)
1771+
{
1772+
if (!PyIter_Check(iter)) {
1773+
PyErr_Format(PyExc_TypeError,
1774+
"'%.100s' object is not an iterator",
1775+
iter->ob_type->tp_name);
1776+
return NULL;
1777+
}
1778+
return (*iter->ob_type->tp_iternext)(iter);
17571779
}

Objects/classobject.c

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -848,7 +848,8 @@ instance_traverse(PyInstanceObject *o, visitproc visit, void *arg)
848848
return 0;
849849
}
850850

851-
static PyObject *getitemstr, *setitemstr, *delitemstr, *lenstr, *iterstr;
851+
static PyObject *getitemstr, *setitemstr, *delitemstr, *lenstr;
852+
static PyObject *iterstr, *nextstr;
852853

853854
static int
854855
instance_length(PyInstanceObject *inst)
@@ -1726,6 +1727,14 @@ instance_getiter(PyInstanceObject *self)
17261727
if ((func = instance_getattr(self, iterstr)) != NULL) {
17271728
PyObject *res = PyEval_CallObject(func, (PyObject *)NULL);
17281729
Py_DECREF(func);
1730+
if (res != NULL && !PyIter_Check(res)) {
1731+
PyErr_Format(PyExc_TypeError,
1732+
"__iter__ returned non-iterator "
1733+
"of type '%.100s'",
1734+
res->ob_type->tp_name);
1735+
Py_DECREF(res);
1736+
res = NULL;
1737+
}
17291738
return res;
17301739
}
17311740
PyErr_Clear();
@@ -1734,7 +1743,33 @@ instance_getiter(PyInstanceObject *self)
17341743
return NULL;
17351744
}
17361745
Py_DECREF(func);
1737-
return PyIter_New((PyObject *)self);
1746+
return PySeqIter_New((PyObject *)self);
1747+
}
1748+
1749+
1750+
/* Call the iterator's next */
1751+
static PyObject *
1752+
instance_iternext(PyInstanceObject *self)
1753+
{
1754+
PyObject *func;
1755+
1756+
if (nextstr == NULL)
1757+
nextstr = PyString_InternFromString("next");
1758+
1759+
if ((func = instance_getattr(self, nextstr)) != NULL) {
1760+
PyObject *res = PyEval_CallObject(func, (PyObject *)NULL);
1761+
Py_DECREF(func);
1762+
if (res != NULL) {
1763+
return res;
1764+
}
1765+
if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
1766+
PyErr_Clear();
1767+
return NULL;
1768+
}
1769+
return NULL;
1770+
}
1771+
PyErr_SetString(PyExc_TypeError, "instance has no next() method");
1772+
return NULL;
17381773
}
17391774

17401775

@@ -1803,6 +1838,7 @@ PyTypeObject PyInstance_Type = {
18031838
instance_richcompare, /* tp_richcompare */
18041839
offsetof(PyInstanceObject, in_weakreflist), /* tp_weaklistoffset */
18051840
(getiterfunc)instance_getiter, /* tp_iter */
1841+
(iternextfunc)instance_iternext, /* tp_iternext */
18061842
};
18071843

18081844

Objects/dictobject.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,6 +1354,7 @@ PyTypeObject PyDict_Type = {
13541354
0, /* tp_richcompare */
13551355
0, /* tp_weaklistoffset */
13561356
(getiterfunc)dictiter_new, /* tp_iter */
1357+
0, /* tp_iternext */
13571358
};
13581359

13591360
/* For backward compatibility with old dictionary interface */
@@ -1433,6 +1434,7 @@ static PyObject *
14331434
dictiter_next(dictiterobject *di, PyObject *args)
14341435
{
14351436
PyObject *key;
1437+
14361438
if (di->di_size != di->di_dict->ma_size) {
14371439
PyErr_SetString(PyExc_RuntimeError,
14381440
"dictionary changed size during iteration");
@@ -1460,9 +1462,25 @@ static PyMethodDef dictiter_methods[] = {
14601462
};
14611463

14621464
static PyObject *
1463-
dictiter_getattr(dictiterobject *it, char *name)
1465+
dictiter_getattr(dictiterobject *di, char *name)
1466+
{
1467+
return Py_FindMethod(dictiter_methods, (PyObject *)di, name);
1468+
}
1469+
1470+
static PyObject *dictiter_iternext(dictiterobject *di)
14641471
{
1465-
return Py_FindMethod(dictiter_methods, (PyObject *)it, name);
1472+
PyObject *key;
1473+
1474+
if (di->di_size != di->di_dict->ma_size) {
1475+
PyErr_SetString(PyExc_RuntimeError,
1476+
"dictionary changed size during iteration");
1477+
return NULL;
1478+
}
1479+
if (PyDict_Next((PyObject *)(di->di_dict), &di->di_pos, &key, NULL)) {
1480+
Py_INCREF(key);
1481+
return key;
1482+
}
1483+
return NULL;
14661484
}
14671485

14681486
PyTypeObject PyDictIter_Type = {
@@ -1494,4 +1512,5 @@ PyTypeObject PyDictIter_Type = {
14941512
0, /* tp_richcompare */
14951513
0, /* tp_weaklistoffset */
14961514
(getiterfunc)dictiter_getiter, /* tp_iter */
1515+
(iternextfunc)dictiter_iternext, /* tp_iternext */
14971516
};

Objects/fileobject.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,7 @@ PyTypeObject PyFile_Type = {
13401340
0, /* tp_richcompare */
13411341
0, /* tp_weaklistoffset */
13421342
(getiterfunc)file_getiter, /* tp_iter */
1343+
0, /* tp_iternext */
13431344
};
13441345

13451346
/* Interface for the 'soft space' between print items. */

Objects/iterobject.c

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ typedef struct {
66
PyObject_HEAD
77
long it_index;
88
PyObject *it_seq;
9-
} iterobject;
9+
} seqiterobject;
1010

1111
PyObject *
12-
PyIter_New(PyObject *seq)
12+
PySeqIter_New(PyObject *seq)
1313
{
14-
iterobject *it;
15-
it = PyObject_NEW(iterobject, &PyIter_Type);
14+
seqiterobject *it;
15+
it = PyObject_NEW(seqiterobject, &PySeqIter_Type);
1616
if (it == NULL)
1717
return NULL;
1818
it->it_index = 0;
@@ -21,21 +21,44 @@ PyIter_New(PyObject *seq)
2121
return (PyObject *)it;
2222
}
2323
static void
24-
iter_dealloc(iterobject *it)
24+
iter_dealloc(seqiterobject *it)
2525
{
2626
Py_DECREF(it->it_seq);
2727
PyObject_DEL(it);
2828
}
2929

3030
static PyObject *
31-
iter_next(iterobject *it, PyObject *args)
31+
iter_next(seqiterobject *it, PyObject *args)
3232
{
3333
PyObject *seq = it->it_seq;
34+
PyObject *result = PySequence_GetItem(seq, it->it_index++);
35+
36+
if (result == NULL && PyErr_ExceptionMatches(PyExc_IndexError))
37+
PyErr_SetObject(PyExc_StopIteration, Py_None);
38+
return result;
39+
}
40+
41+
static PyObject *
42+
iter_getiter(PyObject *it)
43+
{
44+
Py_INCREF(it);
45+
return it;
46+
}
47+
48+
/* Return (value, 0) if OK; (NULL, 0) at end; (NULL, -1) if exception */
49+
static PyObject *
50+
iter_iternext(PyObject *iterator)
51+
{
52+
seqiterobject *it;
53+
PyObject *seq;
54+
55+
assert(PySeqIter_Check(iterator));
56+
it = (seqiterobject *)iterator;
57+
seq = it->it_seq;
3458

3559
if (PyList_Check(seq)) {
3660
PyObject *item;
3761
if (it->it_index >= PyList_GET_SIZE(seq)) {
38-
PyErr_SetObject(PyExc_StopIteration, Py_None);
3962
return NULL;
4063
}
4164
item = PyList_GET_ITEM(seq, it->it_index);
@@ -45,37 +68,37 @@ iter_next(iterobject *it, PyObject *args)
4568
}
4669
else {
4770
PyObject *result = PySequence_GetItem(seq, it->it_index++);
48-
if (result == NULL &&
49-
PyErr_ExceptionMatches(PyExc_IndexError))
50-
PyErr_SetObject(PyExc_StopIteration, Py_None);
51-
return result;
71+
if (result != NULL) {
72+
return result;
73+
}
74+
if (PyErr_ExceptionMatches(PyExc_IndexError) ||
75+
PyErr_ExceptionMatches(PyExc_StopIteration)) {
76+
PyErr_Clear();
77+
return NULL;
78+
}
79+
else {
80+
return NULL;
81+
}
5282
}
5383
}
5484

55-
static PyObject *
56-
iter_getiter(PyObject *it)
57-
{
58-
Py_INCREF(it);
59-
return it;
60-
}
61-
6285
static PyMethodDef iter_methods[] = {
6386
{"next", (PyCFunction)iter_next, METH_VARARGS,
6487
"it.next() -- get the next value, or raise StopIteration"},
6588
{NULL, NULL} /* sentinel */
6689
};
6790

6891
static PyObject *
69-
iter_getattr(iterobject *it, char *name)
92+
iter_getattr(seqiterobject *it, char *name)
7093
{
7194
return Py_FindMethod(iter_methods, (PyObject *)it, name);
7295
}
7396

74-
PyTypeObject PyIter_Type = {
97+
PyTypeObject PySeqIter_Type = {
7598
PyObject_HEAD_INIT(&PyType_Type)
7699
0, /* ob_size */
77100
"iterator", /* tp_name */
78-
sizeof(iterobject), /* tp_basicsize */
101+
sizeof(seqiterobject), /* tp_basicsize */
79102
0, /* tp_itemsize */
80103
/* methods */
81104
(destructor)iter_dealloc, /* tp_dealloc */
@@ -100,6 +123,7 @@ PyTypeObject PyIter_Type = {
100123
0, /* tp_richcompare */
101124
0, /* tp_weaklistoffset */
102125
(getiterfunc)iter_getiter, /* tp_iter */
126+
(iternextfunc)iter_iternext, /* tp_iternext */
103127
};
104128

105129
/* -------------------------------------- */
@@ -130,6 +154,7 @@ calliter_dealloc(calliterobject *it)
130154
Py_DECREF(it->it_sentinel);
131155
PyObject_DEL(it);
132156
}
157+
133158
static PyObject *
134159
calliter_next(calliterobject *it, PyObject *args)
135160
{
@@ -156,6 +181,22 @@ calliter_getattr(calliterobject *it, char *name)
156181
return Py_FindMethod(calliter_methods, (PyObject *)it, name);
157182
}
158183

184+
static PyObject *
185+
calliter_iternext(calliterobject *it)
186+
{
187+
PyObject *result = PyObject_CallObject(it->it_callable, NULL);
188+
if (result != NULL) {
189+
if (PyObject_RichCompareBool(result, it->it_sentinel, Py_EQ)) {
190+
Py_DECREF(result);
191+
result = NULL;
192+
}
193+
}
194+
else if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
195+
PyErr_Clear();
196+
}
197+
return result;
198+
}
199+
159200
PyTypeObject PyCallIter_Type = {
160201
PyObject_HEAD_INIT(&PyType_Type)
161202
0, /* ob_size */
@@ -185,4 +226,5 @@ PyTypeObject PyCallIter_Type = {
185226
0, /* tp_richcompare */
186227
0, /* tp_weaklistoffset */
187228
(getiterfunc)iter_getiter, /* tp_iter */
229+
(iternextfunc)calliter_iternext, /* tp_iternext */
188230
};

0 commit comments

Comments
 (0)