]> granicus.if.org Git - python/commitdiff
Mondo changes to the iterator stuff, without changing how Python code
authorGuido van Rossum <guido@python.org>
Mon, 23 Apr 2001 14:08:49 +0000 (14:08 +0000)
committerGuido van Rossum <guido@python.org>
Mon, 23 Apr 2001 14:08:49 +0000 (14:08 +0000)
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.

Include/abstract.h
Include/iterobject.h
Include/object.h
Objects/abstract.c
Objects/classobject.c
Objects/dictobject.c
Objects/fileobject.c
Objects/iterobject.c
Python/ceval.c

index c56c8876de16ae559b1c8d408dcb72e0550468a9..1dae5f144264489fd37cb7246e76e723507f09ea 100644 (file)
@@ -470,11 +470,24 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
 
        */
 
+/* Iterators */
+
      DL_IMPORT(PyObject *) PyObject_GetIter(PyObject *);
      /* Takes an object and returns an iterator for it.
         This is typically a new iterator but if the argument
        is an iterator, this returns itself. */
 
+#define PyIter_Check(obj) \
+    (PyType_HasFeature((obj)->ob_type, Py_TPFLAGS_HAVE_ITER) && \
+     (obj)->ob_type->tp_iternext != NULL)
+
+     DL_IMPORT(PyObject *) PyIter_Next(PyObject *);
+     /* Takes an iterator object and calls its tp_iternext slot,
+       returning the next value.  If the iterator is exhausted,
+       this can return NULL without setting an exception, *or*
+       NULL with a StopIteration exception.
+       NULL with any other exception  means an error occurred. */
+
 /*  Number Protocol:*/
 
      DL_IMPORT(int) PyNumber_Check(PyObject *o);
index 38454a43c64f0a72c77f2637cbd802dab291a58c..bc189910f9747e3617bb570bad0640a4107b1cf6 100644 (file)
@@ -1,10 +1,10 @@
 /* Iterators (the basic kind, over a sequence) */
 
-extern DL_IMPORT(PyTypeObject) PyIter_Type;
+extern DL_IMPORT(PyTypeObject) PySeqIter_Type;
 
-#define PyIter_Check(op) ((op)->ob_type == &PyIter_Type)
+#define PySeqIter_Check(op) ((op)->ob_type == &PySeqIter_Type)
 
-extern DL_IMPORT(PyObject *) PyIter_New(PyObject *);
+extern DL_IMPORT(PyObject *) PySeqIter_New(PyObject *);
 
 extern DL_IMPORT(PyTypeObject) PyCallIter_Type;
 
index 4a53835116affca5bc71a87670f6244a192e79c8..0765748e9fcd7bf1ba65c795b35fe63796c220a6 100644 (file)
@@ -201,6 +201,7 @@ typedef PyObject *(*reprfunc)(PyObject *);
 typedef long (*hashfunc)(PyObject *);
 typedef PyObject *(*richcmpfunc) (PyObject *, PyObject *, int);
 typedef PyObject *(*getiterfunc) (PyObject *);
+typedef PyObject *(*iternextfunc) (PyObject *);
 
 typedef struct _typeobject {
        PyObject_VAR_HEAD
@@ -252,6 +253,7 @@ typedef struct _typeobject {
 
        /* Iterators */
        getiterfunc tp_iter;
+       iternextfunc tp_iternext;
 
 #ifdef COUNT_ALLOCS
        /* these must be last and never explicitly initialized */
index 8a6df76fc31a5cb63ec97d46d041e5ebcec20a01..f6567479f4221504c6a22b5d7c29106688dfef30 100644 (file)
@@ -1748,10 +1748,32 @@ PyObject_GetIter(PyObject *o)
                f = t->tp_iter;
        if (f == NULL) {
                if (PySequence_Check(o))
-                       return PyIter_New(o);
+                       return PySeqIter_New(o);
                PyErr_SetString(PyExc_TypeError, "iter() of non-sequence");
                return NULL;
        }
-       else
-               return (*f)(o);
+       else {
+               PyObject *res = (*f)(o);
+               if (res != NULL && !PyIter_Check(res)) {
+                       PyErr_Format(PyExc_TypeError,
+                                    "iter() returned non-iterator "
+                                    "of type '%.100s'",
+                                    res->ob_type->tp_name);
+                       Py_DECREF(res);
+                       res = NULL;
+               }
+               return res;
+       }
+}
+
+PyObject *
+PyIter_Next(PyObject *iter)
+{
+       if (!PyIter_Check(iter)) {
+               PyErr_Format(PyExc_TypeError,
+                            "'%.100s' object is not an iterator",
+                            iter->ob_type->tp_name);
+               return NULL;
+       }
+       return (*iter->ob_type->tp_iternext)(iter);
 }
index 742e4729103998edbc786e73c6a0d34130c0103b..80b7ae51f2973cb4a0a419cb0fac31cc169305d4 100644 (file)
@@ -848,7 +848,8 @@ instance_traverse(PyInstanceObject *o, visitproc visit, void *arg)
        return 0;
 }
 
-static PyObject *getitemstr, *setitemstr, *delitemstr, *lenstr, *iterstr;
+static PyObject *getitemstr, *setitemstr, *delitemstr, *lenstr;
+static PyObject *iterstr, *nextstr;
 
 static int
 instance_length(PyInstanceObject *inst)
@@ -1726,6 +1727,14 @@ instance_getiter(PyInstanceObject *self)
        if ((func = instance_getattr(self, iterstr)) != NULL) {
                PyObject *res = PyEval_CallObject(func, (PyObject *)NULL);
                Py_DECREF(func);
+               if (res != NULL && !PyIter_Check(res)) {
+                       PyErr_Format(PyExc_TypeError,
+                                    "__iter__ returned non-iterator "
+                                    "of type '%.100s'",
+                                    res->ob_type->tp_name);
+                       Py_DECREF(res);
+                       res = NULL;
+               }
                return res;
        }
        PyErr_Clear();
@@ -1734,7 +1743,33 @@ instance_getiter(PyInstanceObject *self)
                return NULL;
        }
        Py_DECREF(func);
-       return PyIter_New((PyObject *)self);
+       return PySeqIter_New((PyObject *)self);
+}
+
+
+/* Call the iterator's next */
+static PyObject *
+instance_iternext(PyInstanceObject *self)
+{
+       PyObject *func;
+
+       if (nextstr == NULL)
+               nextstr = PyString_InternFromString("next");
+
+       if ((func = instance_getattr(self, nextstr)) != NULL) {
+               PyObject *res = PyEval_CallObject(func, (PyObject *)NULL);
+               Py_DECREF(func);
+               if (res != NULL) {
+                       return res;
+               }
+               if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
+                       PyErr_Clear();
+                       return NULL;
+               }
+               return NULL;
+       }
+       PyErr_SetString(PyExc_TypeError, "instance has no next() method");
+       return NULL;
 }
 
 
@@ -1803,6 +1838,7 @@ PyTypeObject PyInstance_Type = {
        instance_richcompare,                   /* tp_richcompare */
        offsetof(PyInstanceObject, in_weakreflist), /* tp_weaklistoffset */
        (getiterfunc)instance_getiter,          /* tp_iter */
+       (iternextfunc)instance_iternext,        /* tp_iternext */
 };
 
 
index 17b6a043e2a7cf757ef38136c2feea8f73c6e993..9a24109705702d6a65291bab04e50ae6da4aeaf8 100644 (file)
@@ -1354,6 +1354,7 @@ PyTypeObject PyDict_Type = {
        0,                                      /* tp_richcompare */
        0,                                      /* tp_weaklistoffset */
        (getiterfunc)dictiter_new,              /* tp_iter */
+       0,                                      /* tp_iternext */
 };
 
 /* For backward compatibility with old dictionary interface */
@@ -1433,6 +1434,7 @@ static PyObject *
 dictiter_next(dictiterobject *di, PyObject *args)
 {
        PyObject *key;
+
        if (di->di_size != di->di_dict->ma_size) {
                PyErr_SetString(PyExc_RuntimeError,
                                "dictionary changed size during iteration");
@@ -1460,9 +1462,25 @@ static PyMethodDef dictiter_methods[] = {
 };
 
 static PyObject *
-dictiter_getattr(dictiterobject *it, char *name)
+dictiter_getattr(dictiterobject *di, char *name)
+{
+       return Py_FindMethod(dictiter_methods, (PyObject *)di, name);
+}
+
+static PyObject *dictiter_iternext(dictiterobject *di)
 {
-       return Py_FindMethod(dictiter_methods, (PyObject *)it, name);
+       PyObject *key;
+
+       if (di->di_size != di->di_dict->ma_size) {
+               PyErr_SetString(PyExc_RuntimeError,
+                               "dictionary changed size during iteration");
+               return NULL;
+       }
+       if (PyDict_Next((PyObject *)(di->di_dict), &di->di_pos, &key, NULL)) {
+               Py_INCREF(key);
+               return key;
+       }
+       return NULL;
 }
 
 PyTypeObject PyDictIter_Type = {
@@ -1494,4 +1512,5 @@ PyTypeObject PyDictIter_Type = {
        0,                                      /* tp_richcompare */
        0,                                      /* tp_weaklistoffset */
        (getiterfunc)dictiter_getiter,          /* tp_iter */
+       (iternextfunc)dictiter_iternext,        /* tp_iternext */
 };
index b6cfb928bbb921276ea458455ea2c98031b49a88..70833845bca6635ca63428557d941586762d420c 100644 (file)
@@ -1340,6 +1340,7 @@ PyTypeObject PyFile_Type = {
        0,                                      /* tp_richcompare */
        0,                                      /* tp_weaklistoffset */
        (getiterfunc)file_getiter,              /* tp_iter */
+       0,                                      /* tp_iternext */
 };
 
 /* Interface for the 'soft space' between print items. */
index 616417a35ac91e248de87e90bb4aefdb3bcc91b2..8601980e87c940b6b57a7eb178524f4cd3812ae0 100644 (file)
@@ -6,13 +6,13 @@ typedef struct {
        PyObject_HEAD
        long      it_index;
        PyObject *it_seq;
-} iterobject;
+} seqiterobject;
 
 PyObject *
-PyIter_New(PyObject *seq)
+PySeqIter_New(PyObject *seq)
 {
-       iterobject *it;
-       it = PyObject_NEW(iterobject, &PyIter_Type);
+       seqiterobject *it;
+       it = PyObject_NEW(seqiterobject, &PySeqIter_Type);
        if (it == NULL)
                return NULL;
        it->it_index = 0;
@@ -21,21 +21,44 @@ PyIter_New(PyObject *seq)
        return (PyObject *)it;
 }
 static void
-iter_dealloc(iterobject *it)
+iter_dealloc(seqiterobject *it)
 {
        Py_DECREF(it->it_seq);
        PyObject_DEL(it);
 }
 
 static PyObject *
-iter_next(iterobject *it, PyObject *args)
+iter_next(seqiterobject *it, PyObject *args)
 {
        PyObject *seq = it->it_seq;
+       PyObject *result = PySequence_GetItem(seq, it->it_index++);
+
+       if (result == NULL && PyErr_ExceptionMatches(PyExc_IndexError))
+               PyErr_SetObject(PyExc_StopIteration, Py_None);
+       return result;
+}
+
+static PyObject *
+iter_getiter(PyObject *it)
+{
+       Py_INCREF(it);
+       return it;
+}
+
+/* Return (value, 0) if OK; (NULL, 0) at end; (NULL, -1) if exception */
+static PyObject *
+iter_iternext(PyObject *iterator)
+{
+       seqiterobject *it;
+       PyObject *seq;
+
+       assert(PySeqIter_Check(iterator));
+       it = (seqiterobject *)iterator;
+       seq = it->it_seq;
 
        if (PyList_Check(seq)) {
                PyObject *item;
                if (it->it_index >= PyList_GET_SIZE(seq)) {
-                       PyErr_SetObject(PyExc_StopIteration, Py_None);
                        return NULL;
                }
                item = PyList_GET_ITEM(seq, it->it_index);
@@ -45,20 +68,20 @@ iter_next(iterobject *it, PyObject *args)
        }
        else {
                PyObject *result = PySequence_GetItem(seq, it->it_index++);
-               if (result == NULL &&
-                   PyErr_ExceptionMatches(PyExc_IndexError))
-                       PyErr_SetObject(PyExc_StopIteration, Py_None);
-               return result;
+               if (result != NULL) {
+                       return result;
+               }
+               if (PyErr_ExceptionMatches(PyExc_IndexError) ||
+                       PyErr_ExceptionMatches(PyExc_StopIteration)) {
+                       PyErr_Clear();
+                       return NULL;
+               }
+               else {
+                       return NULL;
+               }
        }
 }
 
-static PyObject *
-iter_getiter(PyObject *it)
-{
-       Py_INCREF(it);
-       return it;
-}
-
 static PyMethodDef iter_methods[] = {
        {"next",        (PyCFunction)iter_next, METH_VARARGS,
         "it.next() -- get the next value, or raise StopIteration"},
@@ -66,16 +89,16 @@ static PyMethodDef iter_methods[] = {
 };
 
 static PyObject *
-iter_getattr(iterobject *it, char *name)
+iter_getattr(seqiterobject *it, char *name)
 {
        return Py_FindMethod(iter_methods, (PyObject *)it, name);
 }
 
-PyTypeObject PyIter_Type = {
+PyTypeObject PySeqIter_Type = {
        PyObject_HEAD_INIT(&PyType_Type)
        0,                                      /* ob_size */
        "iterator",                             /* tp_name */
-       sizeof(iterobject),                     /* tp_basicsize */
+       sizeof(seqiterobject),                  /* tp_basicsize */
        0,                                      /* tp_itemsize */
        /* methods */
        (destructor)iter_dealloc,               /* tp_dealloc */
@@ -100,6 +123,7 @@ PyTypeObject PyIter_Type = {
        0,                                      /* tp_richcompare */
        0,                                      /* tp_weaklistoffset */
        (getiterfunc)iter_getiter,              /* tp_iter */
+       (iternextfunc)iter_iternext,            /* tp_iternext */
 };
 
 /* -------------------------------------- */
@@ -130,6 +154,7 @@ calliter_dealloc(calliterobject *it)
        Py_DECREF(it->it_sentinel);
        PyObject_DEL(it);
 }
+
 static PyObject *
 calliter_next(calliterobject *it, PyObject *args)
 {
@@ -156,6 +181,22 @@ calliter_getattr(calliterobject *it, char *name)
        return Py_FindMethod(calliter_methods, (PyObject *)it, name);
 }
 
+static PyObject *
+calliter_iternext(calliterobject *it)
+{
+       PyObject *result = PyObject_CallObject(it->it_callable, NULL);
+       if (result != NULL) {
+               if (PyObject_RichCompareBool(result, it->it_sentinel, Py_EQ)) {
+                       Py_DECREF(result);
+                       result = NULL;
+               }
+       }
+       else if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
+               PyErr_Clear();
+       }
+       return result;
+}
+
 PyTypeObject PyCallIter_Type = {
        PyObject_HEAD_INIT(&PyType_Type)
        0,                                      /* ob_size */
@@ -185,4 +226,5 @@ PyTypeObject PyCallIter_Type = {
        0,                                      /* tp_richcompare */
        0,                                      /* tp_weaklistoffset */
        (getiterfunc)iter_getiter,              /* tp_iter */
+       (iternextfunc)calliter_iternext,        /* tp_iternext */
 };
index d76c6f2766d942baaf210c336ee0c762251f59a8..d5e3a4e9a16d608326c7eac34a3ac8f238f056d7 100644 (file)
@@ -381,7 +381,6 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals,
        /* Make it easier to find out where we are with a debugger */
        char *filename = PyString_AsString(co->co_filename);
 #endif
-       static PyObject *nextstr;
 
 /* Code access macros */
 
@@ -417,11 +416,6 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals,
                                     GETLOCAL(i) = value; } while (0)
 
 /* Start of code */
-       if (nextstr == NULL) {
-               nextstr = PyString_InternFromString("next");
-               if (nextstr == NULL)
-                       return NULL;
-       }
 
 #ifdef USE_STACKCHECK
        if (tstate->recursion_depth%10 == 0 && PyOS_CheckStack()) {
@@ -1887,34 +1881,29 @@ eval_code2(PyCodeObject *co, PyObject *globals, PyObject *locals,
                        x = PyObject_GetIter(v);
                        Py_DECREF(v);
                        if (x != NULL) {
-                               w = x;
-                               x = PyObject_GetAttr(w, nextstr);
-                               Py_DECREF(w);
-                               if (x != NULL) {
-                                       PUSH(x);
-                                       continue;
-                               }
+                               PUSH(x);
+                               continue;
                        }
                        break;
 
                case FOR_ITER:
                        /* before: [iter]; after: [iter, iter()] *or* [] */
                        v = TOP();
-                       x = PyObject_CallObject(v, NULL);
-                       if (x == NULL) {
-                               if (PyErr_ExceptionMatches(
-                                       PyExc_StopIteration))
-                               {
-                                       PyErr_Clear();
-                                       x = v = POP();
-                                       Py_DECREF(v);
-                                       JUMPBY(oparg);
-                                       continue;
-                               }
-                               break;
+                       x = PyIter_Next(v);
+                       if (x != NULL) {
+                               PUSH(x);
+                               continue;
                        }
-                       PUSH(x);
-                       continue;
+                       if (!PyErr_Occurred() ||
+                           PyErr_ExceptionMatches(
+                                   PyExc_StopIteration))
+                       {
+                               x = v = POP();
+                               Py_DECREF(v);
+                               JUMPBY(oparg);
+                               continue;
+                       }
+                       break;
 
                case FOR_LOOP:
                        /* for v in s: ...