Index: Lib/collections.py =================================================================== --- Lib/collections.py (revision 87228) +++ Lib/collections.py (working copy) @@ -334,6 +334,17 @@ ### Counter ######################################################################## +def _count_elements(mapping, iterable): + 'Tally elements from the iterable.' + mapping_get = mapping.get + for elem in iterable: + mapping[elem] = mapping_get(elem, 0) + 1 + +try: # Load C helper function if available + from _collections import _count_elements +except ImportError: + pass + class Counter(dict): '''Dict subclass for counting hashable items. Sometimes called a bag or multiset. Elements are stored as dictionary keys and their counts @@ -476,9 +487,7 @@ else: dict.update(self, iterable) # fast path when counter is empty else: - self_get = self.get - for elem in iterable: - self[elem] = 1 + self_get(elem, 0) + _count_elements(self, iterable) if kwds: self.update(kwds) Index: Modules/_collectionsmodule.c =================================================================== --- Modules/_collectionsmodule.c (revision 87228) +++ Modules/_collectionsmodule.c (working copy) @@ -1518,6 +1518,70 @@ PyObject_GC_Del, /* tp_free */ }; +/* helper function for Counter *********************************************/ + +PyDoc_STRVAR(_count_elements_doc, +"_count_elements(mapping, iterable) -> None\n\ +\n\ +Count elements in the iterable, updating the mappping"); + +static PyObject * +_count_elements(PyObject *self, PyObject *args) +{ + PyObject *it; + PyObject *oldval; + PyObject *newval = NULL; + PyObject *key = NULL; + PyObject *one = NULL; + PyObject *iterable; + PyObject *mapping; + + if (!PyArg_UnpackTuple(args, "_count_elements", 2, 2, &mapping, &iterable)) + return NULL; + + if (!PyDict_Check(mapping)) { + PyErr_SetString(PyExc_TypeError, "Expected mapping to be a dictionary"); + return NULL; + } + + it = PyObject_GetIter(iterable); + if (it == NULL) + return NULL; + one = PyLong_FromLong(1); + if (one == NULL) { + Py_DECREF(it); + return NULL; + } + while (1) { + key = PyIter_Next(it); + if (key == NULL) { + if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_StopIteration)) + PyErr_Clear(); + break; + } + oldval = PyDict_GetItem(mapping, key); + if (oldval == NULL) { + if(PyDict_SetItem(mapping, key, one) == -1) + break; + } else { + newval = PyNumber_Add(oldval, one); + if (newval == NULL) + break; + if(PyDict_SetItem(mapping, key, newval) == -1) + break; + Py_CLEAR(newval); + } + Py_DECREF(key); + } + Py_DECREF(it); + Py_XDECREF(key); + Py_XDECREF(newval); + Py_DECREF(one); + if (PyErr_Occurred()) + return NULL; + Py_RETURN_NONE; +} + /* module level code ********************************************************/ PyDoc_STRVAR(module_doc, @@ -1526,16 +1590,20 @@ - defaultdict: dict subclass with a default value factory\n\ "); +static struct PyMethodDef module_functions[] = { + {"_count_elements", _count_elements, METH_VARARGS, _count_elements_doc}, + {NULL, NULL} /* sentinel */ +}; static struct PyModuleDef _collectionsmodule = { PyModuleDef_HEAD_INIT, "_collections", module_doc, -1, + module_functions, NULL, NULL, NULL, - NULL, NULL };