A patch to support L.count(value, cmp=None, key=None)

Ping ping.nsr.yeh at gmail.com
Mon Jun 18 13:21:28 EDT 2007


Hi,

I patched Objects/listobject.c to support
  L.count(value, cmp=None, key=None).
I tested it with the same script above by replacing slist
with built-in list.  It worked correctly with this small
test.  The patch is below (126 lines, I hope that's not
too big to be pasted here).  This is the first time that
I modified CPython source, and I may very well make
mistakes in (lack of) reference counting or other things.
Comments and corrections are much appreciated!

Regards,
Ping


--- Objects/listobject.c.orig   Sun Oct 29 05:39:10 2006
+++ Objects/listobject.c        Tue Jun 19 01:04:30 2007
@@ -919,12 +919,12 @@

 /* Comparison function.  Takes care of calling a user-supplied
  * comparison function (any callable Python object), which must not
be
- * NULL (use the ISLT macro if you don't know, or call
PyObject_RichCompareBool
- * with Py_LT if you know it's NULL).
- * Returns -1 on error, 1 if x < y, 0 if x >= y.
+ * NULL.
+ * Returns -9 on error, otherwise return the result of the user-
supplied
+ * comparison.
  */
 static int
-islt(PyObject *x, PyObject *y, PyObject *compare)
+custom_compare(PyObject *x, PyObject *y, PyObject *compare)
 {
        PyObject *res;
        PyObject *args;
@@ -936,7 +936,7 @@
         */
        args = PyTuple_New(2);
        if (args == NULL)
-               return -1;
+               return -9;
        Py_INCREF(x);
        Py_INCREF(y);
        PyTuple_SET_ITEM(args, 0, x);
@@ -944,16 +944,28 @@
        res = PyObject_Call(compare, args, NULL);
        Py_DECREF(args);
        if (res == NULL)
-               return -1;
+               return -9;
        if (!PyInt_Check(res)) {
                Py_DECREF(res);
                PyErr_SetString(PyExc_TypeError,
                                "comparison function must return
int");
-               return -1;
+               return -9;
        }
        i = PyInt_AsLong(res);
        Py_DECREF(res);
-       return i < 0;
+       return i;
+}
+
+/* "less-than" Comparison function.  Calls custom_compare to do the
+ * actual comparison.
+ * Returns -1 on error, 1 if x < y, 0 if x >= y.
+ */
+static int
+islt(PyObject *x, PyObject *y, PyObject *compare)
+{
+       int res = custom_compare(x, y, compare);
+       if (res == -9) return -1;
+       return res < 0;
 }

 /* If COMPARE is NULL, calls PyObject_RichCompareBool with Py_LT,
else calls
@@ -2232,16 +2244,44 @@
 }

 static PyObject *
-listcount(PyListObject *self, PyObject *v)
+listcount(PyListObject *self, PyObject * args, PyObject *kwds)
 {
+       PyObject *v = NULL;             /* value for counting */
+       PyObject *compare = NULL;
+       PyObject *keyfunc = NULL;
+       static char *kwlist[] = {"value", "cmp", "key", 0};
+       PyObject *item;
        Py_ssize_t count = 0;
        Py_ssize_t i;
+       int cmp;
+
+       assert(self != NULL);
+       assert (PyList_Check(self));
+       if (args != NULL) {
+               if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|
OO:count",
+                       kwlist, &v, &compare, &keyfunc))
+                       return NULL;
+       }
+       if (compare == Py_None)
+               compare = NULL;
+       if (keyfunc == Py_None)
+               keyfunc = NULL;

        for (i = 0; i < self->ob_size; i++) {
-               int cmp = PyObject_RichCompareBool(self->ob_item[i],
v, Py_EQ);
+               item = self->ob_item[i];
+               if (keyfunc != NULL) {
+                       item = PyObject_CallFunctionObjArgs(keyfunc,
item,
+                                                          NULL);
+               }
+
+               if (compare != NULL) {
+                       cmp = custom_compare(item, v, compare);
+               } else {
+                       cmp = PyObject_RichCompareBool(item, v,
Py_EQ);
+               }
                if (cmp > 0)
                        count++;
-               else if (cmp < 0)
+               else if (cmp == -9)
                        return NULL;
        }
        return PyInt_FromSsize_t(count);
@@ -2404,7 +2444,7 @@
 PyDoc_STRVAR(index_doc,
 "L.index(value, [start, [stop]]) -> integer -- return first index of
value");
 PyDoc_STRVAR(count_doc,
-"L.count(value) -> integer -- return number of occurrences of
value");
+"L.count(value, cmp=None, key=None) -> integer -- return number of
occurrences of value [in the key] [with the cmp function].");
 PyDoc_STRVAR(reverse_doc,
 "L.reverse() -- reverse *IN PLACE*");
 PyDoc_STRVAR(sort_doc,
@@ -2422,7 +2462,7 @@
        {"pop",         (PyCFunction)listpop,     METH_VARARGS,
pop_doc},
        {"remove",      (PyCFunction)listremove,  METH_O, remove_doc},
        {"index",       (PyCFunction)listindex,   METH_VARARGS,
index_doc},
-       {"count",       (PyCFunction)listcount,   METH_O, count_doc},
+       {"count",       (PyCFunction)listcount,   METH_VARARGS |
METH_KEYWORDS, count_doc},
        {"reverse",     (PyCFunction)listreverse, METH_NOARGS,
reverse_doc},
        {"sort",        (PyCFunction)listsort,    METH_VARARGS |
METH_KEYWORDS, sort_doc},
        {NULL,          NULL}           /* sentinel */




More information about the Python-list mailing list