Issue 10667: Fast path for collections.Counter
This commit is contained in:
parent
779f19e7a7
commit
96f3410ebe
|
@ -334,6 +334,17 @@ def namedtuple(typename, field_names, verbose=False, rename=False):
|
|||
### 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 @@ class Counter(dict):
|
|||
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)
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ Core and Builtins
|
|||
Library
|
||||
-------
|
||||
|
||||
- Issue #10667: Fast path for collections.Counter().
|
||||
|
||||
- Issue #10695: passing the port as a string value to telnetlib no longer
|
||||
causes debug mode to fail.
|
||||
|
||||
|
|
|
@ -1518,6 +1518,68 @@ static PyTypeObject defdict_type = {
|
|||
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, *iterable, *mapping, *oldval;
|
||||
PyObject *newval = NULL;
|
||||
PyObject *key = NULL;
|
||||
PyObject *one = NULL;
|
||||
|
||||
if (!PyArg_UnpackTuple(args, "_count_elements", 2, 2, &mapping, &iterable))
|
||||
return NULL;
|
||||
|
||||
if (!PyDict_Check(mapping)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"Expected mapping argument 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,13 +1588,17 @@ PyDoc_STRVAR(module_doc,
|
|||
- 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,
|
||||
NULL,
|
||||
module_functions,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
|
|
Loading…
Reference in New Issue