summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2001-01-17 15:28:20 (GMT)
committerGuido van Rossum <guido@python.org>2001-01-17 15:28:20 (GMT)
commit8998b4f691db9bf1efcdd1fd496aaea64200a957 (patch)
tree18755b2e10c1166605a8c66a1c90059b1430d7c6 /Objects
parentb226b0c07840d55e91e549deea91d95cdf32c624 (diff)
downloadcpython-8998b4f691db9bf1efcdd1fd496aaea64200a957.zip
cpython-8998b4f691db9bf1efcdd1fd496aaea64200a957.tar.gz
cpython-8998b4f691db9bf1efcdd1fd496aaea64200a957.tar.bz2
Rich comparisons.
- Got rid of instance_cmp(); refactored instance_compare(). - Added instance_richcompare() which calls __lt__() etc. Some unrelated stuff mixed in: - Aligned comments in various large struct initializers. - Better test to avoid recursion if __coerce__ returns self as the first argument (this is an unrelated fix by Neil Schemenauer!). - Style nit: don't use Py_DECREF(Py_NotImplemented); use Py_DECREF(result) -- it just looks better. :-)
Diffstat (limited to 'Objects')
-rw-r--r--Objects/classobject.c424
1 files changed, 278 insertions, 146 deletions
diff --git a/Objects/classobject.c b/Objects/classobject.c
index 4dc72d2..7f76d6e 100644
--- a/Objects/classobject.c
+++ b/Objects/classobject.c
@@ -386,24 +386,24 @@ PyTypeObject PyClass_Type = {
"class",
sizeof(PyClassObject) + PyGC_HEAD_SIZE,
0,
- (destructor)class_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- (reprfunc)class_repr, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- 0, /*tp_hash*/
- 0, /*tp_call*/
- (reprfunc)class_str, /*tp_str*/
- (getattrofunc)class_getattr, /*tp_getattro*/
- (setattrofunc)class_setattr, /*tp_setattro*/
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /*tp_flags*/
- 0, /* tp_doc */
- (traverseproc)class_traverse, /* tp_traverse */
+ (destructor)class_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc)class_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ (reprfunc)class_str, /* tp_str */
+ (getattrofunc)class_getattr, /* tp_getattro */
+ (setattrofunc)class_setattr, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */
+ 0, /* tp_doc */
+ (traverseproc)class_traverse, /* tp_traverse */
};
int
@@ -909,9 +909,9 @@ instance_ass_subscript(PyInstanceObject *inst, PyObject *key, PyObject *value)
}
static PyMappingMethods instance_as_mapping = {
- (inquiry)instance_length, /*mp_length*/
- (binaryfunc)instance_subscript, /*mp_subscript*/
- (objobjargproc)instance_ass_subscript, /*mp_ass_subscript*/
+ (inquiry)instance_length, /* mp_length */
+ (binaryfunc)instance_subscript, /* mp_subscript */
+ (objobjargproc)instance_ass_subscript, /* mp_ass_subscript */
};
static PyObject *
@@ -1134,14 +1134,14 @@ static int instance_contains(PyInstanceObject *inst, PyObject *member)
static PySequenceMethods
instance_as_sequence = {
- (inquiry)instance_length, /*sq_length*/
- 0, /*sq_concat*/
- 0, /*sq_repeat*/
- (intargfunc)instance_item, /*sq_item*/
- (intintargfunc)instance_slice, /*sq_slice*/
- (intobjargproc)instance_ass_item, /*sq_ass_item*/
- (intintobjargproc)instance_ass_slice, /*sq_ass_slice*/
- (objobjproc)instance_contains, /* sq_contains */
+ (inquiry)instance_length, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ (intargfunc)instance_item, /* sq_item */
+ (intintargfunc)instance_slice, /* sq_slice */
+ (intobjargproc)instance_ass_item, /* sq_ass_item */
+ (intintobjargproc)instance_ass_slice, /* sq_ass_slice */
+ (objobjproc)instance_contains, /* sq_contains */
};
static PyObject *
@@ -1232,10 +1232,10 @@ half_binop(PyObject *v, PyObject *w, char *opname, binaryfunc thisfunc,
}
v1 = PyTuple_GetItem(coerced, 0);
w = PyTuple_GetItem(coerced, 1);
- if (v1 == v) {
+ if (v1->ob_type == v->ob_type && PyInstance_Check(v)) {
/* prevent recursion if __coerce__ returns self as the first
* argument */
- result = generic_binary_op(v, w, opname);
+ result = generic_binary_op(v1, w, opname);
} else {
if (swapped)
result = (thisfunc)(w, v1);
@@ -1253,7 +1253,7 @@ do_binop(PyObject *v, PyObject *w, char *opname, char *ropname,
{
PyObject *result = half_binop(v, w, opname, thisfunc, 0);
if (result == Py_NotImplemented) {
- Py_DECREF(Py_NotImplemented);
+ Py_DECREF(result);
result = half_binop(w, v, ropname, thisfunc, 1);
}
return result;
@@ -1265,7 +1265,7 @@ do_binop_inplace(PyObject *v, PyObject *w, char *iopname, char *opname,
{
PyObject *result = half_binop(v, w, iopname, thisfunc, 0);
if (result == Py_NotImplemented) {
- Py_DECREF(Py_NotImplemented);
+ Py_DECREF(result);
result = do_binop(v, w, opname, ropname, thisfunc);
}
return result;
@@ -1371,62 +1371,119 @@ BINARY_INPLACE(instance_imul, "mul", PyNumber_InPlaceMultiply)
BINARY_INPLACE(instance_idiv, "div", PyNumber_InPlaceDivide)
BINARY_INPLACE(instance_imod, "mod", PyNumber_InPlaceRemainder)
-static PyObject *
-do_cmp(PyObject *v, PyObject *w)
+/* Try a 3-way comparison, returning an int; v is an instance. Return:
+ -2 for an exception;
+ -1 if v < w;
+ 0 if v == w;
+ 1 if v > w;
+ 2 if this particular 3-way comparison is not implemented or undefined.
+*/
+static int
+half_cmp(PyObject *v, PyObject *w)
{
- int cmp = PyObject_Compare(v, w);
- if (PyErr_Occurred()) {
- return NULL;
+ static PyObject *cmp_obj;
+ PyObject *args;
+ PyObject *cmpfunc;
+ PyObject *result;
+ long l;
+
+ assert(PyInstance_Check(v));
+
+ if (cmp_obj == NULL) {
+ cmp_obj = PyString_InternFromString("__cmp__");
+ if (cmp_obj == NULL)
+ return -2;
}
- return PyInt_FromLong(cmp);
-}
-static PyObject *
-instance_cmp(PyObject *v, PyObject *w)
-{
- PyObject *result = half_binop(v, w, "__cmp__", do_cmp, 0);
+ cmpfunc = PyObject_GetAttr(v, cmp_obj);
+ if (cmpfunc == NULL) {
+ PyErr_Clear();
+ return 2;
+ }
+
+ args = Py_BuildValue("(O)", w);
+ if (args == NULL)
+ return -2;
+
+ result = PyEval_CallObject(cmpfunc, args);
+ Py_DECREF(args);
+ Py_DECREF(cmpfunc);
+
+ if (result == NULL)
+ return -2;
+
if (result == Py_NotImplemented) {
- Py_DECREF(Py_NotImplemented);
- /* __rcmp__ is not called on instances, instead they
- * automaticly reverse the arguments and return the negative of
- * __cmp__ if it exists */
- result = half_binop(w, v, "__cmp__", do_cmp, 0);
-
- if (result != Py_NotImplemented && result != NULL) {
- PyObject *r = PyNumber_Negative(result);
- Py_DECREF(result);
- result = r;
- }
+ Py_DECREF(result);
+ return 2;
}
- return result;
+
+ l = PyInt_AsLong(result);
+ Py_DECREF(result);
+ if (l == -1 && PyErr_Occurred()) {
+ PyErr_SetString(PyExc_TypeError,
+ "comparison did not return an int");
+ return -2;
+ }
+
+ return l < 0 ? -1 : l > 0 ? 1 : 0;
}
+/* Try a 3-way comparison, returning an int; either v or w is an instance.
+ We first try a coercion. Return:
+ -2 for an exception;
+ -1 if v < w;
+ 0 if v == w;
+ 1 if v > w;
+ 2 if this particular 3-way comparison is not implemented or undefined.
+ THIS IS ONLY CALLED FROM object.c!
+*/
static int
-instance_compare(PyObject *inst, PyObject *other)
+instance_compare(PyObject *v, PyObject *w)
{
- PyObject *result;
- long outcome;
- result = instance_cmp(inst, other);
- if (result == NULL) {
- return -1;
+ int c;
+
+ c = PyNumber_CoerceEx(&v, &w);
+ if (c < 0)
+ return -2;
+ if (c == 0) {
+ /* If neither is now an instance, use regular comparison */
+ if (!PyInstance_Check(v) && !PyInstance_Check(w)) {
+ c = PyObject_Compare(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ if (PyErr_Occurred())
+ return -2;
+ return c < 0 ? -1 : c > 0 ? 1 : 0;
+ }
}
- if (result == Py_NotImplemented) {
- Py_DECREF(result);
- return -1;
+ else {
+ /* The coercion didn't do anything.
+ Treat this the same as returning v and w unchanged. */
+ Py_INCREF(v);
+ Py_INCREF(w);
}
- if (!PyInt_Check(result)) {
- Py_DECREF(result);
- PyErr_SetString(PyExc_TypeError,
- "comparison did not return an int");
- return -1;
+
+ if (PyInstance_Check(v)) {
+ c = half_cmp(v, w);
+ if (c <= 1) {
+ Py_DECREF(v);
+ Py_DECREF(w);
+ return c;
+ }
}
- outcome = PyInt_AsLong(result);
- Py_DECREF(result);
- if (outcome < 0)
- return -1;
- else if (outcome > 0)
- return 1;
- return 0;
+ if (PyInstance_Check(w)) {
+ c = half_cmp(w, v);
+ if (c <= 1) {
+ Py_DECREF(v);
+ Py_DECREF(w);
+ if (c >= -1)
+ c = -c;
+ return c;
+ }
+ }
+ Py_DECREF(v);
+ Py_DECREF(w);
+ return 2;
}
static int
@@ -1550,43 +1607,116 @@ instance_ipow(PyObject *v, PyObject *w, PyObject *z)
}
+/* Map rich comparison operators to their __xx__ namesakes */
+static char *name_op[] = {
+ "__lt__",
+ "__le__",
+ "__eq__",
+ "__ne__",
+ "__gt__",
+ "__ge__",
+};
+
+static PyObject *
+half_richcompare(PyObject *v, PyObject *w, int op)
+{
+ PyObject *name;
+ PyObject *method;
+ PyObject *args;
+ PyObject *res;
+
+ assert(PyInstance_Check(v));
+
+ name = PyString_InternFromString(name_op[op]);
+ if (name == NULL)
+ return NULL;
+
+ method = PyObject_GetAttr(v, name);
+ Py_DECREF(name);
+ if (method == NULL) {
+ if (!PyErr_ExceptionMatches(PyExc_AttributeError))
+ return NULL;
+ PyErr_Clear();
+ res = Py_NotImplemented;
+ Py_INCREF(res);
+ return res;
+ }
+
+ args = Py_BuildValue("(O)", w);
+ if (args == NULL) {
+ Py_DECREF(method);
+ return NULL;
+ }
+
+ res = PyEval_CallObject(method, args);
+ Py_DECREF(args);
+ Py_DECREF(method);
+
+ return res;
+}
+
+/* Map rich comparison operators to their swapped version, e.g. LT --> GT */
+static int swapped_op[] = {Py_GT, Py_GE, Py_EQ, Py_NE, Py_LT, Py_LE};
+
+static PyObject *
+instance_richcompare(PyObject *v, PyObject *w, int op)
+{
+ PyObject *res;
+
+ if (PyInstance_Check(v)) {
+ res = half_richcompare(v, w, op);
+ if (res != Py_NotImplemented)
+ return res;
+ Py_DECREF(res);
+ }
+
+ if (PyInstance_Check(w)) {
+ res = half_richcompare(w, v, swapped_op[op]);
+ if (res != Py_NotImplemented)
+ return res;
+ Py_DECREF(res);
+ }
+
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+}
+
static PyNumberMethods instance_as_number = {
- (binaryfunc)instance_add, /*nb_add*/
- (binaryfunc)instance_sub, /*nb_subtract*/
- (binaryfunc)instance_mul, /*nb_multiply*/
- (binaryfunc)instance_div, /*nb_divide*/
- (binaryfunc)instance_mod, /*nb_remainder*/
- (binaryfunc)instance_divmod, /*nb_divmod*/
- (ternaryfunc)instance_pow, /*nb_power*/
- (unaryfunc)instance_neg, /*nb_negative*/
- (unaryfunc)instance_pos, /*nb_positive*/
- (unaryfunc)instance_abs, /*nb_absolute*/
- (inquiry)instance_nonzero, /*nb_nonzero*/
- (unaryfunc)instance_invert, /*nb_invert*/
- (binaryfunc)instance_lshift, /*nb_lshift*/
- (binaryfunc)instance_rshift, /*nb_rshift*/
- (binaryfunc)instance_and, /*nb_and*/
- (binaryfunc)instance_xor, /*nb_xor*/
- (binaryfunc)instance_or, /*nb_or*/
- (coercion)instance_coerce, /*nb_coerce*/
- (unaryfunc)instance_int, /*nb_int*/
- (unaryfunc)instance_long, /*nb_long*/
- (unaryfunc)instance_float, /*nb_float*/
- (unaryfunc)instance_oct, /*nb_oct*/
- (unaryfunc)instance_hex, /*nb_hex*/
- (binaryfunc)instance_iadd, /*nb_inplace_add*/
- (binaryfunc)instance_isub, /*nb_inplace_subtract*/
- (binaryfunc)instance_imul, /*nb_inplace_multiply*/
- (binaryfunc)instance_idiv, /*nb_inplace_divide*/
- (binaryfunc)instance_imod, /*nb_inplace_remainder*/
- (ternaryfunc)instance_ipow, /*nb_inplace_power*/
- (binaryfunc)instance_ilshift, /*nb_inplace_lshift*/
- (binaryfunc)instance_irshift, /*nb_inplace_rshift*/
- (binaryfunc)instance_iand, /*nb_inplace_and*/
- (binaryfunc)instance_ixor, /*nb_inplace_xor*/
- (binaryfunc)instance_ior, /*nb_inplace_or*/
- (binaryfunc)instance_cmp, /*nb_cmp*/
+ (binaryfunc)instance_add, /* nb_add */
+ (binaryfunc)instance_sub, /* nb_subtract */
+ (binaryfunc)instance_mul, /* nb_multiply */
+ (binaryfunc)instance_div, /* nb_divide */
+ (binaryfunc)instance_mod, /* nb_remainder */
+ (binaryfunc)instance_divmod, /* nb_divmod */
+ (ternaryfunc)instance_pow, /* nb_power */
+ (unaryfunc)instance_neg, /* nb_negative */
+ (unaryfunc)instance_pos, /* nb_positive */
+ (unaryfunc)instance_abs, /* nb_absolute */
+ (inquiry)instance_nonzero, /* nb_nonzero */
+ (unaryfunc)instance_invert, /* nb_invert */
+ (binaryfunc)instance_lshift, /* nb_lshift */
+ (binaryfunc)instance_rshift, /* nb_rshift */
+ (binaryfunc)instance_and, /* nb_and */
+ (binaryfunc)instance_xor, /* nb_xor */
+ (binaryfunc)instance_or, /* nb_or */
+ (coercion)instance_coerce, /* nb_coerce */
+ (unaryfunc)instance_int, /* nb_int */
+ (unaryfunc)instance_long, /* nb_long */
+ (unaryfunc)instance_float, /* nb_float */
+ (unaryfunc)instance_oct, /* nb_oct */
+ (unaryfunc)instance_hex, /* nb_hex */
+ (binaryfunc)instance_iadd, /* nb_inplace_add */
+ (binaryfunc)instance_isub, /* nb_inplace_subtract */
+ (binaryfunc)instance_imul, /* nb_inplace_multiply */
+ (binaryfunc)instance_idiv, /* nb_inplace_divide */
+ (binaryfunc)instance_imod, /* nb_inplace_remainder */
+ (ternaryfunc)instance_ipow, /* nb_inplace_power */
+ (binaryfunc)instance_ilshift, /* nb_inplace_lshift */
+ (binaryfunc)instance_irshift, /* nb_inplace_rshift */
+ (binaryfunc)instance_iand, /* nb_inplace_and */
+ (binaryfunc)instance_ixor, /* nb_inplace_xor */
+ (binaryfunc)instance_ior, /* nb_inplace_or */
};
PyTypeObject PyInstance_Type = {
@@ -1595,24 +1725,26 @@ PyTypeObject PyInstance_Type = {
"instance",
sizeof(PyInstanceObject) + PyGC_HEAD_SIZE,
0,
- (destructor)instance_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- instance_compare, /*tp_compare*/
- (reprfunc)instance_repr, /*tp_repr*/
- &instance_as_number, /*tp_as_number*/
- &instance_as_sequence, /*tp_as_sequence*/
- &instance_as_mapping, /*tp_as_mapping*/
- (hashfunc)instance_hash, /*tp_hash*/
- 0, /*tp_call*/
- 0, /*tp_str*/
- (getattrofunc)instance_getattr, /*tp_getattro*/
- (setattrofunc)instance_setattr, /*tp_setattro*/
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC | Py_TPFLAGS_NEWSTYLENUMBER, /*tp_flags*/
- 0, /* tp_doc */
+ (destructor)instance_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ instance_compare, /* tp_compare */
+ (reprfunc)instance_repr, /* tp_repr */
+ &instance_as_number, /* tp_as_number */
+ &instance_as_sequence, /* tp_as_sequence */
+ &instance_as_mapping, /* tp_as_mapping */
+ (hashfunc)instance_hash, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ (getattrofunc)instance_getattr, /* tp_getattro */
+ (setattrofunc)instance_setattr, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC | Py_TPFLAGS_CHECKTYPES,/*tp_flags*/
+ 0, /* tp_doc */
(traverseproc)instance_traverse, /* tp_traverse */
+ 0, /* tp_clear */
+ instance_richcompare, /* tp_richcompare */
};
@@ -1854,23 +1986,23 @@ PyTypeObject PyMethod_Type = {
"instance method",
sizeof(PyMethodObject) + PyGC_HEAD_SIZE,
0,
- (destructor)instancemethod_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- (cmpfunc)instancemethod_compare, /*tp_compare*/
- (reprfunc)instancemethod_repr, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- (hashfunc)instancemethod_hash, /*tp_hash*/
- 0, /*tp_call*/
- 0, /*tp_str*/
- (getattrofunc)instancemethod_getattro, /*tp_getattro*/
- (setattrofunc)instancemethod_setattro, /*tp_setattro*/
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /*tp_flags*/
- 0, /* tp_doc */
+ (destructor)instancemethod_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ (cmpfunc)instancemethod_compare, /* tp_compare */
+ (reprfunc)instancemethod_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ (hashfunc)instancemethod_hash, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ (getattrofunc)instancemethod_getattro, /* tp_getattro */
+ (setattrofunc)instancemethod_setattro, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /* tp_flags */
+ 0, /* tp_doc */
(traverseproc)instancemethod_traverse, /* tp_traverse */
};