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 */ -- http://mail.python.org/mailman/listinfo/python-list