[Python-checkins] r87265 - in python/branches/py3k: Lib/collections.py Misc/NEWS Modules/_collectionsmodule.c

raymond.hettinger python-checkins at python.org
Wed Dec 15 17:30:37 CET 2010


Author: raymond.hettinger
Date: Wed Dec 15 17:30:37 2010
New Revision: 87265

Log:
Issue 10667: Fast path for collections.Counter

Modified:
   python/branches/py3k/Lib/collections.py
   python/branches/py3k/Misc/NEWS
   python/branches/py3k/Modules/_collectionsmodule.c

Modified: python/branches/py3k/Lib/collections.py
==============================================================================
--- python/branches/py3k/Lib/collections.py	(original)
+++ python/branches/py3k/Lib/collections.py	Wed Dec 15 17:30:37 2010
@@ -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)
 

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Wed Dec 15 17:30:37 2010
@@ -17,6 +17,8 @@
 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.
 

Modified: python/branches/py3k/Modules/_collectionsmodule.c
==============================================================================
--- python/branches/py3k/Modules/_collectionsmodule.c	(original)
+++ python/branches/py3k/Modules/_collectionsmodule.c	Wed Dec 15 17:30:37 2010
@@ -1518,6 +1518,68 @@
     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 @@
 - 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,


More information about the Python-checkins mailing list