I was reading into the proper way to consume an iterator on this question as well as here and I notices there is no actual proper function for this that clearly explains itself.
Using:
collections.deque(maxlen=0).extend(iterable)
isn't exactly very descriptive whereas making a properly named method for this that just uses the deque
approach is good I wanted to do my own research as well as learn some c (which I've rarely ever actually dealt with) along the way.
So.. Using the approach that is used in _collectionsmodule.c I have come up with this:
#include "Python.h"
#include <ctype.h>
static PyObject *
consume(PyObject *self, PyObject *iterable)
{
PyObject *(*iternext)(PyObject *);
PyObject *item;
if (PyObject_GetIter(iterable) == NULL)
return NULL;
iternext = *Py_TYPE(iterable)->tp_iternext;
while ((item = iternext(iterable)) != NULL)
Py_DECREF(item);
if (PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_StopIteration))
PyErr_Clear();
else
return NULL;
}
Py_RETURN_NONE;
}
static PyMethodDef consumeMethods[] = {
{"consume", (PyCFunction)consume, METH_O,
"Run an iterator to exhaustion."},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef _consumemodule = {
PyModuleDef_HEAD_INIT,
"_consumemodule",
"Consume Module",
-1,
consumeMethods,
NULL,
NULL,
NULL,
NULL
};
PyMODINIT_FUNC
PyInit__consumemodule(void)
{
return PyModule_Create(&_consumemodule);
}
It performs marginally better than the deque approach which is expected as it's using almost the same approach although I was wondering if any of the c guru's out there could see if I'm missing anything or if something could be done better as all it's doing it exhausting the iterator. I tried using METH_FASTCALL
but that doesn't perform as well as METH_O
and in the while
loop I tried usingwhile ((Py_XDECREF(iternext(iterable))) != NULL)
the only way I could find to eliminate the need for the variable item
was by making a macro like this:
static inline int _Ret_DECREF(PyObject *op)
{
if (op == NULL)
return 0;
Py_DECREF(op);
return 1;
}
#define Ret_DECREF(op) _Ret_DECREF(op)
Then just looping through like this:
while (1) {
if (Ret_DECREF(iternext(iterable)))
break;
}
But something just feels off doing that.
more_itertools.consume
- which uses the exact same code. \$\endgroup\$