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

tim@lassi.ece.uiuc.edu tim@lassi.ece.uiuc.edu
Thu, 24 Oct 1996 15:24:31 -0500


I wrote:

>The way errors are handled could still be cleaned up, but not today... 

I lied. I tried, but I couldn't stop myself from cleaning it up. Let
me describe the effects of the latest patch. Hopefully this will
alleviate some of Konrad's concerns. I'd really appreciate it some
people could try this out and give me comments (and find all my bugs
for me ;)

As I've looked into this more, I've decided that this idea is more
useful than I first thought. It fixes lots of silent errors that I
didn't even know existed. For instance, a user defined __cmp__
function can return any object it wants, but if its not an integer,
the compare function just ignores it and does something
arbitrary. Also, exceptions in __cmp__ are currently ignored (and
there was such a fuss about exceptions in __del__ methods too...).

How does this apply to NumPy? Assuming it (or something like it) gets
adopted someday, it gives us two new options for comparing
arrays:

1) (easy) Raise an exception - if any arrays are compared an exception
would be raised. Note that if arrays were the same object, they still
would evaluate as equal instead of raising an exception.

2) (better) Return 0 for arrays that are equal, and NOT_EQUAL for
arrays that are not equal. This would give == and != useful meaning,
but (essentially**) disable the other comparisons.

(**) All comparisons would work if the objects were equal, only if they
were NOT_EQUAL would exceptions get raised. This is seems weird at
first, but is logical if you think about it.

----------------------------------------------------------------

Python __cmp__ functions:
------------------------

Legal return values: integers from -sys.maxint to sys.maxint inclusive
and None. (returning -sys.maxint-1 == INT_MIN or any non-int raises an
exception)

Integers have their normal meanings, None means that the compared
objects are not equal but have no ordering (NOT_EQUAL).

Exceptions raised in __cmp__ methods now work.

If there is no __cmp__ (or __rcmp__) function for either object, the
objects are NOT_EQUAL (unless they are the same object).


C extension class comparsion functions:
-------------------------------------

returning INT_MIN means that the compared objects are NOT_EQUAL.

If errors are set, they are no longer ignored.

If objects cannot be coerced into comparable types, they are NOT_EQUAL.


Assorted:
--------

Objects are always equal to themselves (cmp is not called in this
case). This is unchanged from before.

<,>,<=,>= raise exceptions if their operands are NOT_EQUAL.

list.sort() raises an exception if any of its operands are
NOT_EQUAL. It also correctly propogates errors raised by __cmp__, etc.

cmp(a,b) returns None if the objects are NOT_EQUAL. Exceptions are
propogated correctly.

----------------------------------------------------------------

At least that is what is supposed to happen. If you find otherwise
please let me know, and I'll fix it.

I think this fixes most of the problems with comparisons in a pretty
satisfactory way. I know using INT_MIN gives Konrad the creeps, but
it's only in the C API; it should be completely invisible from the
python end of things.


(This patch is against the original python 1.4b3 sources.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


*** Objects//object.c.orig	Wed Oct 23 13:24:45 1996
--- Objects//object.c	Thu Oct 24 12:07:20 1996
***************
*** 24,29 ****
--- 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 )
***************
*** 258,310 ****
  
  int
  cmpobject(v, w)
! 	object *v, *w;
  {
! 	typeobject *tp;
! 	if (v == w)
! 		return 0;
! 	if (v == NULL)
! 		return -1;
! 	if (w == NULL)
! 		return 1;
! 	if (is_instanceobject(v) || is_instanceobject(w)) {
! 		object *res;
! 		int c;
! 		if (!is_instanceobject(v))
! 			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);
! 		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) {
! 			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);
! 				DECREF(v);
! 				DECREF(w);
! 				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);
  }
  
  long
--- 259,326 ----
  
  int
  cmpobject(v, w)
!     object *v, *w;
  {
!     typeobject *tp;
!     if (v == w)
! 	return 0;
!     if (v == NULL)
! 	return -1;
!     if (w == NULL)
! 	return 1;
!     if (is_instanceobject(v) || is_instanceobject(w)) {
! 	object *res;
! 	int c;
! 	if (!is_instanceobject(v))
! 	    return -cmpobject(w, v);
! 	res = do_cmp(v, w);
! 	if (err_occurred()) { /* Err in __cmp__ */
! 	    object *exc, *val, *tb;
! 	    err_fetch(&exc, &val, &tb);
! 	    if ( (exc != TypeError) || /* if __cmp__ not defined, ignore error */
! 				/* XXX This next is fragile / kludgy, but what to do? */
! 		 strcmp(GETSTRINGVALUE((stringobject *)(val)),  
! 			"__cmp__ nor __rcmp__ defined for these operands") ) {
! 		err_restore(exc, val, tb);
! 	    }
! 	    return INT_MIN;
  	}
!         if (res == NULL)	/* Null result with no error! */
! 	    err_setstr(SystemError, "NULL result without error in call_object");
! 
! 	if (res == None) {	/* NOT_EQUAL */
! 	    return INT_MIN;
  	}
! 	if (!is_intobject(res)) { /* Illegal object returned */
! 	    err_setstr(TypeError, "Illegal return value from __cmp__ or __rcmp__");
! 	    DECREF(res);
! 	    return INT_MIN;
! 	}
! 	c = getintvalue(res);
! 	DECREF(res);
! 	if (c == INT_MIN) { /* Not a legal value for __cmp__ */
! 	    err_setstr(TypeError, "Illegal return value from __cmp__ or __rcmp__");
! 	    return INT_MIN;
! 	}
! 	return c; /* was: 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) {
! 	    if (coerce(&v, &w) != 0) {
! 		return INT_MIN;
! 	    }
! 	    else {
! 		int cmp = (*v->ob_type->tp_compare)(v, w);
! 		DECREF(v);
! 		DECREF(w);
! 		return cmp;
! 	    }
! 	}
! 	return INT_MIN;
!     }
!     if (tp->tp_compare == NULL)
! 	return INT_MIN;
!     return (*tp->tp_compare)(v, w);
  }
  
  long
*** Objects//listobject.c.orig	Wed Oct 23 18:39:31 1996
--- Objects//listobject.c	Thu Oct 24 14:15:26 1996
***************
*** 24,29 ****
--- 24,34 ----
  
  /* List object implementation */
  
+ #include <limits.h>
+ #ifdef roundup
+ #undef roundup
+ #endif
+ 
  #include "allobjects.h"
  #include "modsupport.h"
  #include "ceval.h"
***************
*** 555,562 ****
  	if (err_occurred())
  		return 0;
  
! 	if (comparefunc == NULL)
! 		return cmpobject(* (object **) v, * (object **) w);
  
  	/* Call the user-supplied comparison function */
  	t = mkvalue("(OO)", * (object **) v, * (object **) w);
--- 560,573 ----
  	if (err_occurred())
  		return 0;
  
! 	if (comparefunc == NULL) {
! 	        int cmp_result = cmpobject(* (object **) v, * (object **) w);
! 		if (cmp_result == INT_MIN && !err_occurred()) {
! 		    err_setstr(TypeError, "Illegal types for ordered comparison in sort");
! 		    return 0;
! 		}
! 		return cmp_result;
! 	}
  
  	/* Call the user-supplied comparison function */
  	t = mkvalue("(OO)", * (object **) v, * (object **) w);
*** Python//ceval.c.orig	Wed Oct 23 13:20:18 1996
--- Python//ceval.c	Thu Oct 24 10:51:51 1996
***************
*** 31,36 ****
--- 31,38 ----
     XXX document it!
     */
  
+ #include <limits.h>
+ 
  #include "allobjects.h"
  
  #include "compile.h"
***************
*** 2805,2810 ****
--- 2807,2818 ----
  		break;
  	default:
  		cmp = cmpobject(v, w);
+ 	        if (err_occurred()) 
+ 		    return NULL;
+ 		if (op != EQ && op != NE && cmp == INT_MIN) {
+ 		    err_setstr(TypeError, "Illegal types for ordered comparison");
+ 		    return NULL;
+ 		}
  		switch (op) {
  		case LT: res = cmp <  0; break;
  		case LE: res = cmp <= 0; break;
*** Python//bltinmodule.c.orig	Wed Oct 23 16:26:05 1996
--- Python//bltinmodule.c	Thu Oct 24 14:42:57 1996
***************
*** 24,29 ****
--- 24,30 ----
  
  /* Built-in functions */
  
+ #include <limits.h>
  #include "allobjects.h"
  
  #include "node.h"
***************
*** 229,238 ****
  	object *args;
  {
  	object *a, *b;
! 
  	if (!newgetargs(args, "OO:cmp", &a, &b))
  		return NULL;
! 	return newintobject((long)cmpobject(a, b));
  }
  
  static object *
--- 230,246 ----
  	object *args;
  {
  	object *a, *b;
! 	int cmp_result;
  	if (!newgetargs(args, "OO:cmp", &a, &b))
  		return NULL;
! 	cmp_result = cmpobject(a, b);
! 	if( err_occurred() )
! 	    return NULL;
! 	if(cmp_result == INT_MIN) {
! 	    INCREF(None);
! 	    return None;
! 	}
! 	return newintobject((long)cmp_result);
  }
  
  static object *







++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

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

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