Rich Comparisons Gotcha

Robert Kern robert.kern at gmail.com
Sun Dec 7 00:57:56 CET 2008


Terry Reedy wrote:
> Rasmus Fogh wrote:
>> Dear All,
>>
>> For the first time I have come across a Python feature that seems
>> completely wrong. After the introduction of rich comparisons, equality
>> comparison does not have to return a truth value, and may indeed return
>> nothing at all and throw an error instead. As a result, code like
>>   if foo == bar:
>> or
>>   foo in alist
>> cannot be relied on to work.
>>
>> This is clearly no accident. According to the documentation all 
>> comparison
>> operators are allowed to return non-booleans, or to throw errors. 
>> There is
>> explicitly no guarantee that x == x is True.
> 
> You have touched on a real and known issue that accompanies dynamic 
> typing and the design of Python.  *Every* Python function can return any 
> Python object and may raise any exception either actively, by design, or 
> passively, by not catching exceptions raised in the functions *it* calls.
> 
>> Personally I would like to get these !@#$%&* misfeatures removed,
> 
> What you are calling a misfeature is an absence, not a presence that can 
> be removed.

That's not quite true. Rich comparisons explicitly allow non-boolean return 
values. Breaking up __cmp__ into multiple __special__ methods was not the sole 
purpose of rich comparisons. One of the prime examples at the time was numpy 
(well, Numeric at the time). We wanted to use == to be able to return an array 
with boolean values where the two operand arrays were equal. E.g.

In [1]: from numpy import *

In [2]: array([1, 2, 3]) == array([4, 2, 3])
Out[2]: array([False,  True,  True], dtype=bool)

SQLAlchemy uses these operators to build up objects that will be turned into SQL 
expressions.

 >>> print users.c.id==addresses.c.user_id
users.id = addresses.user_id

Basically, the idea was to turn these operators into full-fledged operators like 
+-/*. Returning a non-boolean violates neither the letter, nor the spirit of the 
feature.

Unfortunately, if you do overload __eq__ to build up expressions or whatnot, the 
other places where users of __eq__ are implicitly expecting a boolean break. 
While I was (and am) a supporter of rich comparisons, I feel Rasmus's pain from 
time to time. It would be nice to have an alternate method to express the 
boolean "yes, this thing is equal in value to that other thing". Unfortunately, 
I haven't figured out a good way to fit it in now without sacrificing rich 
comparisons entirely.

>> and constrain the __eq__ function to always return a truth value.
> 
> It is impossible to do that with certainty by any mechanical 
> creation-time checking.  So the implementation of operator.eq would have 
> to check the return value of the ob.__eq__ function it calls *every 
> time*.  That would slow down the speed of the 99.xx% of cases where the 
> check is not needed and would still not prevent exceptions.  And if the 
> return value was bad, all operator.eq could do is raise and exception 
> anyway.

Sure, but then it would be a bug to return a non-boolean from __eq__ and 
friends. It is not a bug today. I think that's what Rasmus is proposing.

-- 
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
  that is made terrible by our own mad attempt to interpret it as though it had
  an underlying truth."
   -- Umberto Eco




More information about the Python-list mailing list