Rich Comparisons Gotcha

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Sun Dec 7 09:44:55 EST 2008


On Sun, 07 Dec 2008 13:03:43 +0000, Rasmus Fogh wrote:

> Jamed Stroud Wrote:
...
>> Second, consider that any value in python also evaluates to a truth
>> value in boolean context.

But bool(x) can fail too. So not every object in Python can be 
interpreted as a truth value.


>> Third, every function returns something. 

Unless it doesn't return at all.


>> A function's returning nothing
>> is not a possibility in the python language. None is something but
>> evaluates to False in boolean context.
> 
> Indeed. The requirement would be not that return_value was a boolean,
> but that bool(return_value) was defined and gave the correct result.

If __bool__ or __nonzero__ raises an exception, you would like Python to 
ignore the exception and return True or False. Which should it be? How do 
you know what the correct result should be?

>From the Zen of Python:

"In the face of ambiguity, refuse the temptation to guess."


All binary operators are ambiguous when dealing with vector or array 
operands. Should the operator operate on the array as a whole, or on each 
element? The numpy people have decided that element-wise equality testing 
is more useful for them, and this is their prerogative to do so. In fact, 
the move to rich comparisons was driven by the needs of numpy. 

http://www.python.org/dev/peps/pep-0207/

It is a *VERY* important third-party library, and this was not the first 
and probably won't be the last time that their needs will move into 
Python the language.

Python encourages such domain-specific behaviour. In fact, that's what 
operator-overloading is all about: classes can define what any operator 
means for *them*. There's no requirement that the infinity of potential 
classes must all define operators in a mutually compatible fashion, not 
even for comparison operators.

For example, consider a class implementing one particular version of 
three-value logic. It isn't enough for == to only return True or False, 
because you also need Maybe:

True == False => returns False
True == True => returns True
True == Maybe => returns Maybe
etc.

Or consider fuzzy logic, where instead of two truth values, you have a 
continuum of truth values between 0.0 and 1.0. What should comparing two 
such fuzzy values for equality return? A boolean True/False? Another 
fuzzy value?


Another one from the Zen:

"Special cases aren't special enough to break the rules."

The rules are that classes can customize their behaviour, that methods 
can fail, and that Python should not try to guess what the correct value 
should have been in the event of such a failure. Equality is a special 
case, but it isn't so special that it needs to be an exception from those 
rules.

If you really need a guaranteed-can't-fail[1] equality test, try 
something like this untested wrapper class:

class EqualityWrapper(object):
    def __init__(self, obj):
        self.wrapped = obj
    def __eq__(self, other):
        try:
            return bool(self.wrapped == other)
        except Exception:
            return False  # or maybe True?

Now wrap all your data:

data = [a list of arbitrary objects]
data = map(EqualityWrapper, data)
process(data)




[1] Not a guarantee.

-- 
Steven



More information about the Python-list mailing list