[PYTHON MATRIX-SIG] More thoughts on comparsion operators.

tim@lassi.ece.uiuc.edu tim@lassi.ece.uiuc.edu
Wed, 23 Oct 1996 16:39:52 -0500


I spent some more time thinking about comparisons and had a dangerous
thought about comparisons:

"No valid comparison function will ever return (-sys.max-1), otherwise
known as INT_MIN. This is because comparison is a symmetric operation
and cmp(a,b) = -cmp(b,a)."

I thought I might be able to use this to make ordered comparisons a
little safer. That is, to raise exceptions for ordered comparisons that
make no sense instead of returning an arbitrary value. In particular,
I was thinking of >, <, etc. on matrices. However this also applies to
other cases such as comparing strings to ints.

So, armed with this thought I dove into the Python core and started
slicing and dicing. After a certain amount of trial and error, I
implemented the following:

(For clarity let's call INT_MIN, NOT_EQUAL).

In ceval.c: comp_outcome:

NOT_EQUAL means that the operands are not equal, but do not have an
ordering. An exception is raised if the result of cmp is NOT_EQUAL, and
the operation is >,<,>=,or <=. Equal returns false, and not-equal
returns true.

In object.c: compobject:

I nuked all the cases where the comparison was being made on what
appeared to be arbitrary grounds (address, class name, etc.) and
returned NOT_EQUAL. The exception is that objects with the same adress
are still always equal (That doesn't seem arbitrary, just ordering
items based on address does).

In bltinmodule.c: builtin_cmp:

I had this catch NOT_EQUAL and return None, so python users don't ever
see -2147483648 floating around. (compobject catches None and returns
NOT_EQUAL, so this should work).

In arrayobject.c: array_compare:

Since I didn't have the code to check if arrays are equal (Jim?), I
just claimed that arrays are always NOT_EQUAL.

Suprisingly, the interpreter survived this surgery, and I haven't
gotten it to break yet (I've done make test, Num_Py/test/test_all, and
other random scripts.) If anyone is interested in testing this out and
seeing how it works for them, I've included the patches at the end.

Here's some samples of the new behaviour:

>>> print 5 < 6
1
>>> print 5 == 5.
1
>>> print 5 < 'a'
Traceback (innermost last):
  File "<stdin>", line 1, in ?
TypeError: Illegal types for ordered comparison
>>> print 5 == 'a'
0
>>> 5 < 6
1
>>> 5 == 5.
1
>>> 5 == 'a'
0
>>> 5 < 'b'
Traceback (innermost last):
  File "<stdin>", line 1, in ?
TypeError: Illegal types for ordered comparison
>>> class Spam:
...     pass
... 
>>> 5 == Spam
0
>>> s = Spam()
>>> 5 == s
0
>>> s < Spam
Traceback (innermost last):
  File "<stdin>", line 1, in ?
TypeError: __cmp__ nor __rcmp__ defined for these operands
>>> s < 5
Traceback (innermost last):
  File "<stdin>", line 1, in ?
TypeError: __cmp__ nor __rcmp__ defined for these operands
>>> Spam < s
Traceback (innermost last):
  File "<stdin>", line 1, in ?
TypeError: __cmp__ nor __rcmp__ defined for these operands
class Eggs:
...    def __cmp__(self,other):
...             if type(other) != type(self):
...                     return cmp(5, other)
... 
>>> e = Eggs()
>>> e < 7
1
>>> e < 4
0


Did you actually read this far? I'm impressed!!!! Well here's the
patches. Let me know if you think they're interesting / useful.

================================================================


*** Python/bltinmodule.c	Wed Oct 23 16:12:10 1996
--- Python/bltinmodule.c.orig	Wed Oct 23 16:26:05 1996
***************
*** 24,30 ****
  
  /* Built-in functions */
  
- #include <limits.h>
  #include "allobjects.h"
  
  #include "node.h"
--- 24,29 ----
***************
*** 230,244 ****
  	object *args;
  {
  	object *a, *b;
! 	int cmp_result;
  	if (!newgetargs(args, "OO:cmp", &a, &b))
  		return NULL;
! 	cmp_result = cmpobject(a, b);
! 	if(cmp_result == INT_MIN) {
! 	    INCREF(None);
! 	    return None;
! 	}
! 	return newintobject((long)cmp_result);
  }
  
  static object *
--- 229,238 ----
  	object *args;
  {
  	object *a, *b;
! 
  	if (!newgetargs(args, "OO:cmp", &a, &b))
  		return NULL;
! 	return newintobject((long)cmpobject(a, b));
  }
  
  static object *
*** Python/ceval.c	Wed Oct 23 15:32:13 1996
--- Python/ceval.c.orig	Wed Oct 23 13:20:18 1996
***************
*** 31,38 ****
     XXX document it!
     */
  
- #include <limits.h>
- 
  #include "allobjects.h"
  
  #include "compile.h"
--- 31,36 ----
***************
*** 2807,2820 ****
  		break;
  	default:
  		cmp = cmpobject(v, w);
- 		if (op != EQ && op != NE && cmp == INT_MIN) {
- 		    if (!err_occurred())
- 			err_setstr(TypeError, "Illegal types for ordered comparison");
- 		    return NULL;
- 		}
- 		else {
- 		    err_clear(); /* Might have been set. */
- 		}
  		switch (op) {
  		case LT: res = cmp <  0; break;
  		case LE: res = cmp <= 0; break;
--- 2805,2810 ----
*** Objects/object.c	Wed Oct 23 13:30:50 1996
--- Objects/object.c.orig	Wed Oct 23 13:24:45 1996
***************
*** 24,30 ****
  
  /* Generic object operations; and implementation of None (NoObject) */
  
- #include <limits.h>
  #include "allobjects.h"
  
  #if defined( Py_TRACE_REFS ) || defined( Py_REF_DEBUG )
--- 24,29 ----
***************
*** 275,285 ****
  			return -cmpobject(w, v);
  		res = do_cmp(v, w);
  		if (res == NULL) {
! 		    return INT_MIN;
  		}
  		if (!is_intobject(res)) {
  			DECREF(res);
! 			return INT_MIN;
  		}
  		c = getintvalue(res);
  		DECREF(res);
--- 274,285 ----
  			return -cmpobject(w, v);
  		res = do_cmp(v, w);
  		if (res == NULL) {
! 			err_clear();
! 			return (v < w) ? -1 : 1;
  		}
  		if (!is_intobject(res)) {
  			DECREF(res);
! 			return (v < w) ? -1 : 1;
  		}
  		c = getintvalue(res);
  		DECREF(res);
***************
*** 289,295 ****
  		if (tp->tp_as_number != NULL &&
  				w->ob_type->tp_as_number != NULL) {
  			if (coerce(&v, &w) != 0) {
! 			    return INT_MIN;
  			}
  			else {
  				int cmp = (*v->ob_type->tp_compare)(v, w);
--- 289,297 ----
  		if (tp->tp_as_number != NULL &&
  				w->ob_type->tp_as_number != NULL) {
  			if (coerce(&v, &w) != 0) {
! 				err_clear();
! 				/* XXX Should report the error,
! 				   XXX but the interface isn't there... */
  			}
  			else {
  				int cmp = (*v->ob_type->tp_compare)(v, w);
***************
*** 298,307 ****
  				return cmp;
  			}
  		}
! 		return INT_MIN;
  	}
  	if (tp->tp_compare == NULL)
! 		return INT_MIN;
  	return (*tp->tp_compare)(v, w);
  }
  
--- 300,309 ----
  				return cmp;
  			}
  		}
! 		return strcmp(tp->tp_name, w->ob_type->tp_name);
  	}
  	if (tp->tp_compare == NULL)
! 		return (v < w) ? -1 : 1;
  	return (*tp->tp_compare)(v, w);
  }
  



================================================================

-- 
	-tim

+--------------------------------------------------------------------+
| Tim Hochberg               Ultrahigh Speed Digital Electronics Lab |
| tim@lassi.ece.uiuc.edu              University of Illinois         |
| http://dogbert.ece.uiuc.edu/~tim         (217) 333-6014            |
+--------------------------------------------------------------------+





=================
MATRIX-SIG  - SIG on Matrix Math for Python

send messages to: matrix-sig@python.org
administrivia to: matrix-sig-request@python.org
=================