[Python-Dev] Coercion and comparison of numbers (original) (raw)

M.-A. Lemburg mal@lemburg.com
Wed, 02 May 2001 09:59:03 +0200


Neil Schemenauer wrote:

[MAL] > I just received a bug report for mx.Number which revealed a > probelm with the comparison code in Python 2.1. Looking at > the code it seems that one of my original coercion patches > did not make it into the core. I added a new API PyNumberCompare() > knows about the new coercion mechanism and should be called for > numbers instead of trying coercion in PyObjectCompare(). I remember the API. I don't remember what happened to it. Guido might have dropped it or I might have taken it out thinking the comparison issues would be sorted out by Guido.

Good; so there's a chance for getting it back in :-)

Why is a new API needed? Why can't PyObjectCompare() do the right thing (ie. not coerce new style numbers)?

I think the reason for implementing number compares as separate API was to simply shift out code from PyObject_Compare() into a new function, not so much motivated by some higher level need to do number compares.

[Guido]

> Was this part of the coercion patch left out on purpose or > a simple oversight ? I hope the latter...

Hard to say. I don't think I paid very close attention to your patch; Neil did, but I changed a lot of the code around coercions and comparisons in order to implement rich comparisons. So, several things may have happened: Neil lost it; Neil decided against it; or I ripped it out. Can you elucidate me regarding the issues? (If there's code, please quote it or link to a specific patch.) Since the concept of "number" is ill-defined at best, when exactly should PyNumberCompare() be called? What is it supposed to do? Does it need a rich cousin?

The reasoning is simple: the coercion patches basically pass control over coercion down to the APIs in question and thus provide the type with more information to choose from.

This is currently implemented in 2.1 for all number methods, but not for number comparisons which do have the same problems with centralized coercion as e.g. add or other binary operators.

Here's part of the original patch:

--- Include/orig/abstract.h Wed May 13 00:28:58 1998 +++ Include/abstract.h Thu May 21 12:31:55 1998 @@ -447,11 +447,18 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

  This function always succeeds.

    */

[...]

}

+/* Emulate old method for comparing numeric types using coercion and

+{

+} + +PyObject * +PyNumber_Compare(v, w) + PyObject *v, *w; +{ + DPRINTF("PyNumber_Compare(%s at 0x%lx, %s at 0x%lx);\n", + v->ob_type->tp_name,(long)v, + w->ob_type->tp_name,(long)w); + BINOP("cmp", "rcmp", PyNumber_Compare); + return _PyNumber_BinaryOperation(v,w, + NB_SLOT(nb_cmp), + "cmp()"); +} +

[...]

+static PyObject * +_PyNumber_BinaryOperation(PyObject *v, + PyObject *w, + const int op_slot, + const char *operation) +{ + PyNumberMethods *mv, *mw; + register PyObject *x; + register binaryfunc slot; + int c; ... + / When using old coercion, make sure that the requested slot + is available on old style numbers or use an emulation. / + if (op_slot > NB_SLOT(nb_hex)) { + + / Emulation hooks: */ + if (op_slot == NB_SLOT(nb_cmp)) + return _PyNumber_OldstyleCompare(v,w); + + goto badOperands; + }

[...]

int PyObject_Compare(v, w) PyObject *v, *w; { PyTypeObject *tp; @@ -291,27 +294,30 @@ PyObject_Compare(v, w) Py_DECREF(res); PyErr_SetString(PyExc_TypeError, "comparison did not return an int"); return -1; } - c = PyInt_AsLong(res); + c = PyInt_AS_LONG(res); Py_DECREF(res); return (c < 0) ? -1 : (c > 0) ? 1 : 0; } if ((tp = v->ob_type) != w->ob_type) { - if (tp->tp_as_number != NULL && - w->ob_type->tp_as_number != NULL) { - int err; - err = PyNumber_CoerceEx(&v, &w); - if (err < 0) + if (tp->tp_as_number != NULL || + w->ob_type->tp_as_number != NULL) { + PyObject *res; + int c; + res = PyNumber_Compare(v,w); + if (res == NULL) return -1; - else if (err == 0) { - int cmp = (*v->ob_type->tp_compare)(v, w); - Py_DECREF(v); - Py_DECREF(w); - return cmp; + if (!PyInt_Check(res)) { + PyErr_SetString(PyExc_TypeError, + "comparison did not return an int"); + return -1; } + c = PyInt_AS_LONG(res); + Py_DECREF(res); + return (c < 0) ? -1 : (c > 0) ? 1 : 0; } return strcmp(tp->tp_name, w->ob_type->tp_name); } if (tp->tp_compare == NULL) return (v < w) ? -1 : 1;

-- Marc-Andre Lemburg


Company & Consulting: http://www.egenix.com/ Python Software: http://www.lemburg.com/python/