Would there be support for a more general cmp/__cmp__

Christopher Subich csubich.spam.block at spam.subich.block.com
Fri Oct 21 14:10:30 EDT 2005


Antoon Pardon wrote:
> It would be better if cmp would give an indication it
> can't compare two objects instead of giving incorrect
> and inconsistent results.

If two objects aren't totally comparable, then using 'cmp' on them is 
ill-defined to begin with.  The Standard Thing To Do is throw an 
exception; see the Highly Obscure Case of the Complex Numbers.

 >>>1 == 1j
False
 >>>1 != 1j
True
 >>>1 < 1j
Traceback (most recent call last):
   File "<stdin>", line 1, in ?
TypeError: cannot compare complex numbers using <, <=, >, >=
 >>>cmp(1j,1j)
0
 >>>cmp(1,1j)
Traceback (most recent call last):
   File "<stdin>", line 1, in ?
TypeError: cannot compare complex numbers using <, <=, >, >=

So using the well-known case of complex numbers, the semantics are 
already well-defined.

 >>> class Incomparable:
...     def __cmp__(self,other):
...             raise TypeError("cannot compare Incomparables using <, 
<=, >, >=")
...     def __eq__(self,other):
...             return self is other
...     def __neq__(self,other):
...             return self is not other
 >>> a1 = Incomparable()
 >>> a2 = Incomparable()
 >>> a1 == a1
1 # I'm running on python 2.2.3 at the moment, so hence the 1.
 >>> a2 == a2
1
 >>> a1 == a2
0
 >>> a1 < a2
Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 3, in __cmp__
TypeError: cannot compare Incomparables using <, <=, >, >=
 >>> cmp(a1,a2)
Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 3, in __cmp__
TypeError: cannot compare Incomparables using <, <=, >, >=


So your use-case is already well-defined, and rather simple.  Make 
__cmp__ raise an exception of your choice, and define rich comparators 
only for the comparisons that are supported.  If, as you say in another 
post, "some" pairs in D cross D are comparable on an operator but not 
all of them (and further that this graph is not transitive), then your 
_ONLY_ choices, no matter your implementation, is to return some 
possibly inconsistent result (a < b == 1, b < c == 1, a < c == 0) or 
raise an exception for unapplicable comparisons.

This isn't a Python problem; this is a problem from formal mathematics.

Personally, I'd favor the "raise an exception" case, which is exactly 
what will happen if you define rich comparisons and let cmp throw an 
exception.  Operators that assume comparable objects, like sort, also 
almost always assume a total order -- inconsistent operations can give 
weird results, while throwing an exception will at least (usually) give 
some sort of error that can be caught.

Another poster mentioned the B-tree example, and that isn't solvable in 
this case -- B-trees assume a total order, but by their nature aren't 
explicit about checking for it; inserting a "partial plus exception" 
order might result in tree failure at weird times.  An inconsistent 
order, however, is even worse -- it would corrupt the tree at the same 
times.



More information about the Python-list mailing list