Boilerplate in rich comparison methods

Paul McGuire ptmcg at austin.rr._bogus_.com
Sat Jan 13 11:04:17 EST 2007


"George Sakkis" <george.sakkis at gmail.com> wrote in message 
news:1168674664.792869.213260 at m58g2000cwm.googlegroups.com...
> Steven D'Aprano wrote:
>
>> I'm writing a class that implements rich comparisons, and I find myself
>> writing a lot of very similar code. If the calculation is short and
>> simple, I do something like this:
>>
>>
>> class Parrot:
>>     def __eq__(self, other):
>>         return self.plumage() == other.plumage()
<snip>
>> If the comparison requires a lot of work, I'll do something like this:
>>
>> class Aardvark:
>>     def __le__(self, other):
>>         return lots_of_work(self, other)
>>     def __gt__(self, other):
>>         return not self <= other
<snip>
> Once upon a time I had written a metaclass to generate the boilerate,
> filling in the gaps. It doesn't impose any constraints on which
> comparisons you must implement, e.g you may implement __le__ and
> __eq__, or __gt__ and __ne__, etc. Season to taste.
>
> http://rafb.net/p/mpvsIQ37.nln.html
>
> George
>

Just a side note on writing these comparison operators.  I remember when 
learning Java that this was really the first time I spent so much time 
reading about testing-for-identity vs. testing-for-equality.  The Java 
conventional practice at the time was to begin each test-for-equality method 
by testing to see if an object were being compared against itself, and if 
so, cut to the chase and return True (and the converse for an inequality 
comparison).  The idea behind this was that there were ostensibly many times 
in code where an object was being compared against itself (not so much in an 
explicit "if x==x" but in implicit tests such as list searching and 
filtering), and this upfront test-for-identity, being very fast, could 
short-circuit an otherwise needless comparison.

In Python, this would look like:

class Parrot:
    def __eq__(self, other):
        return self is other or self.plumage() == other.plumage()
    def __ne__(self, other):
        return self is not other and self.plumage() != other.plumage()
    def __lt__(self, other):
        return self is not other and self.plumage() < other.plumage()
    def __gt__(self, other):
        return self is not other and self.plumage() > other.plumage()
    def __le__(self, other):
        return self is not other and self.plumage() <= other.plumage()
    def __ge__(self, other):
        return self is not other and self.plumage() >= other.plumage()

and George's metaclass would have similar changes.

On the other hand, I haven't seen this idiom in any Python code that I've 
read, and I wonder if this was just a coding fad of the time.

Still, in cases such as Steven's Aardark class, it might be worth bypassing 
something that calls lots_of_work if you tested first to see if self is not 
other.

-- Paul





More information about the Python-list mailing list