473,396 Members | 2,011 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

Boilerplate in rich comparison methods

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()
def __ne__(self, other):
return self.plumage() != other.plumage()
def __lt__(self, other):
return self.plumage() < other.plumage()
def __gt__(self, other):
return self.plumage() other.plumage()
def __le__(self, other):
return self.plumage() <= other.plumage()
def __ge__(self, other):
return self.plumage() >= other.plumage()

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
# etc.

But I can't help feeling that there is a better way. What do others do?

--
Steven.

Jan 13 '07 #1
11 1564
Steven D'Aprano <st***@REMOVE.THIS.cybersource.com.auwrites:
class Parrot:
def __eq__(self, other):
return self.plumage() == other.plumage()
def __ne__(self, other):
return self.plumage() != other.plumage()
def __lt__(self, other):
return self.plumage() < other.plumage()
def __gt__(self, other):
return self.plumage() other.plumage()
def __le__(self, other):
return self.plumage() <= other.plumage()
def __ge__(self, other):
return self.plumage() >= other.plumage()
If it's that uniform I think you can just use __cmp__:

class Parrot:
def __cmp__(self, other):
return cmp(self.plumage(), other.plumage())

Did I miss something? The idea of rich comparison is that the
different relations aren't so similar to each other, e.g. some kind of
partial ordering.
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
# etc.

But I can't help feeling that there is a better way. What do others do?
Same as above.
Jan 13 '07 #2
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()
def __ne__(self, other):
return self.plumage() != other.plumage()
def __lt__(self, other):
return self.plumage() < other.plumage()
def __gt__(self, other):
return self.plumage() other.plumage()
def __le__(self, other):
return self.plumage() <= other.plumage()
def __ge__(self, other):
return self.plumage() >= other.plumage()

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
# etc.

But I can't help feeling that there is a better way. What do others do?
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

Jan 13 '07 #3
"George Sakkis" <ge***********@gmail.comwrote in message
news:11**********************@m58g2000cwm.googlegr oups.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
Jan 13 '07 #4
"Paul McGuire" <pt***@austin.rr._bogus_.comwrote:
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.
It is a perfectly reasonable short-cut for those types where you know an
object is equal to itself, but that isn't always the case. e.g. floating
point NaN values are not equal to themselves, and a list of numbers might
contain a NaN which would mean the list wouldn't be equal to itself.

Also note that for the __le__, __ge__ cases you got the shortcut test the
wrong way round.
Jan 13 '07 #5
On Jan 13, 12:52 am, Steven D'Aprano
<s...@REMOVE.THIS.cybersource.com.auwrote:
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()
def __ne__(self, other):
return self.plumage() != other.plumage()
def __lt__(self, other):
return self.plumage() < other.plumage()
def __gt__(self, other):
return self.plumage() other.plumage()
def __le__(self, other):
return self.plumage() <= other.plumage()
def __ge__(self, other):
return self.plumage() >= other.plumage()

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
# etc.

But I can't help feeling that there is a better way. What do others do?
Typically, I write only two kinds of classes that use comparion
operators: (1) ones that can get by with __cmp__ and (2) ones that
define __eq__ and __ne__ without any of the other four.

But for your case, I'd say you're doing it the right way. If you
define a lot of classes like Parrot, you might want to try moving the
six operators to a common base class:

class Comparable:
"""
Abstract base class for classes using rich comparisons.
Objects are compared using their cmp_key() method.
"""
def __eq__(self, other):
return (self is other) or (self.cmp_key() == other.cmp_key())
def __ne__(self, other):
return (self is not other) and (self.cmp_key() !=
other.cmp_key())
def __lt__(self, other):
return self.cmp_key() < other.cmp_key()
def __le__(self, other):
return self.cmp_key() <= other.cmp_key()
def __gt__(self, other):
return self.cmp_key() other.cmp_key()
def __ge__(self, other):
return self.cmp_key() >= other.cmp_key()
def cmp_key(self):
"""Overriden by derived classes to define a comparison key."""
raise NotImplementedError()

class Parrot(Comparable):
def cmp_key(self):
return self.plumage()
# ...

Jan 13 '07 #6
On Sat, 13 Jan 2007 10:04:17 -0600, Paul McGuire wrote:
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()
[snip]

Surely this is only worth doing if the comparison is expensive?
Testing beats intuition, so let's find out...

class Compare:
def __init__(self, x):
self.x = x
def __eq__(self, other):
return self.x == other.x

class CompareWithIdentity:
def __init__(self, x):
self.x = x
def __eq__(self, other):
return self is other or self.x == other.x

Here's the timing results without the identity test:
>>import timeit
x = Compare(1); y = Compare(1)
timeit.Timer("x = x", "from __main__ import x,y").repeat()
[0.20771503448486328, 0.16396403312683105, 0.16507196426391602]
>>timeit.Timer("x = y", "from __main__ import x,y").repeat()
[0.20918107032775879, 0.16187810897827148, 0.16351795196533203]

And with the identity test:
>>x = CompareWithIdentity(1); y = CompareWithIdentity(1)
timeit.Timer("x = x", "from __main__ import x,y").repeat()
[0.20761799812316895, 0.16907095909118652, 0.16420602798461914]
>>timeit.Timer("x = y", "from __main__ import x,y").repeat()
[0.2090909481048584, 0.1968839168548584, 0.16479206085205078]

Anyone want to argue that this is a worthwhile optimization? :)
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.
The comparison itself would have to be quite expensive to make it worth
the extra code.
--
Steven.

Jan 13 '07 #7
On Fri, 12 Jan 2007 23:28:06 -0800, Paul Rubin wrote:
Steven D'Aprano <st***@REMOVE.THIS.cybersource.com.auwrites:
>class Parrot:
def __eq__(self, other):
return self.plumage() == other.plumage()
def __ne__(self, other):
return self.plumage() != other.plumage()
def __lt__(self, other):
return self.plumage() < other.plumage()
[snip more boilerplate code]
If it's that uniform I think you can just use __cmp__:
Good point -- I had somehow picked up the mistaken idea that __cmp__ was
depreciated in favour of rich comparisons.
--
Steven.

Jan 13 '07 #8
On 2007-01-13, Steven D'Aprano <st***@REMOVE.THIS.cybersource.com.auwrote:
On Sat, 13 Jan 2007 10:04:17 -0600, Paul McGuire wrote:
[snip]

Surely this is only worth doing if the comparison is expensive?
Testing beats intuition, so let's find out...

class Compare:
def __init__(self, x):
self.x = x
def __eq__(self, other):
return self.x == other.x

class CompareWithIdentity:
def __init__(self, x):
self.x = x
def __eq__(self, other):
return self is other or self.x == other.x

Here's the timing results without the identity test:
>>>import timeit
x = Compare(1); y = Compare(1)
timeit.Timer("x = x", "from __main__ import x,y").repeat()
[0.20771503448486328, 0.16396403312683105, 0.16507196426391602]
>>>timeit.Timer("x = y", "from __main__ import x,y").repeat()
[0.20918107032775879, 0.16187810897827148, 0.16351795196533203]

And with the identity test:
>>>x = CompareWithIdentity(1); y = CompareWithIdentity(1)
timeit.Timer("x = x", "from __main__ import x,y").repeat()
[0.20761799812316895, 0.16907095909118652, 0.16420602798461914]
>>>timeit.Timer("x = y", "from __main__ import x,y").repeat()
[0.2090909481048584, 0.1968839168548584, 0.16479206085205078]

Anyone want to argue that this is a worthwhile optimization? :)
Perhaps. But first test it with "==".

--
Neil Cerutti
Jan 13 '07 #9
On Sat, 13 Jan 2007 22:05:53 +0000, Neil Cerutti wrote:
>Anyone want to argue that this is a worthwhile optimization? :)

Perhaps. But first test it with "==".
Oh the ignominy! That's what happens when I run code at 6am :(
>>x = CompareWithIdentity(1); y = CompareWithIdentity(1)
timeit.Timer("x == y", "from __main__ import x,y").repeat()
[2.2971229553222656, 2.2821698188781738, 2.2767620086669922]
>>timeit.Timer("x == x", "from __main__ import x,y").repeat()
[1.6935880184173584, 1.6783449649810791, 1.6613109111785889]
>>x = Compare(1); y = Compare(1)
timeit.Timer("x == y", "from __main__ import x,y").repeat()
[2.1717329025268555, 2.1361908912658691, 2.1338419914245605]

So for this simple case, testing for identity is a factor of 1.3 faster
when the objects are identical, and a factor of 1.1 slower if they aren't.
That suggests that if about 33% of your comparisons match by identity,
you'll break-even; any less than that, and the optimization is actually a
pessimation.

--
Steven.

Jan 14 '07 #10
"Steven D'Aprano" <st***@REMOVE.THIS.cybersource.com.auescribió en el
mensaje
news:pa****************************@REMOVE.THIS.cy bersource.com.au...
On Fri, 12 Jan 2007 23:28:06 -0800, Paul Rubin wrote:
>If it's that uniform I think you can just use __cmp__:

Good point -- I had somehow picked up the mistaken idea that __cmp__ was
depreciated in favour of rich comparisons.
If you inherit from a base class that implements rich comparisons, you have
to override all those methods, else your __cmp__ won't be called at all.
Sometimes it may be enough to implement __cmp__ and make all others call it.

--
Gabriel Genellina

Jan 14 '07 #11
On 2007-01-13, Steven D'Aprano <st***@REMOVE.THIS.cybersource.com.auwrote:
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()
def __ne__(self, other):
return self.plumage() != other.plumage()
def __lt__(self, other):
return self.plumage() < other.plumage()
def __gt__(self, other):
return self.plumage() other.plumage()
def __le__(self, other):
return self.plumage() <= other.plumage()
def __ge__(self, other):
return self.plumage() >= other.plumage()
Well one thing you could do is write the following class:

Comparators = SomeEnumWith("eq, ne, lt, gt, ge, le, ge")

class GeneralComparator:
def __eq__(self, other):
return Comparators.eq in self.__compare__(self, other)
def __ne__(self, other):
return Comparators.ne in self.__compare__(self, other)
def __lt__(self, other):
return Comparators.lt in self.__compare__(self, other)
def __le__(self, other):
return Comparators.le in self.__compare__(self, other)
def __gt__(self, other):
return Comparators.gt in self.__compare__(self, other)
def __ge__(self, other):
return Comparators.ge in self.__compare__(self, other)
Then write your Parrot class as follows:

class Parrot (GeneralComparator):
def __compare__(self, other):
return a set which defines which comparisons should return true.

--
Antoon Pardon
Jan 15 '07 #12

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

5
by: Simon Burton | last post by:
In today's experiment, I was wondering if I could make the comparison operators (<,<=,>=,>) work on classes (types) according to inheritance. The idea is, for example, classes lower in the class...
3
by: Stian Søiland | last post by:
Could anyone explain to me why this does not work as intended? >>> class Oldstyle: .... pass .... >>> old = Oldstyle() >>> old == 1 False >>> old.__eq__ = lambda x: True >>> old == 1
4
by: John Hunter | last post by:
In matplotlib, a plotting library with an OO API and a matlab-like procedural interface, I have a lot of functions defined in the matlab interface module that wrap similarly named class methods...
4
by: Dim | last post by:
I found that C# has some buggy ways to process string across methods. I have a class with on global string var and a method where i add / remove from this string Consider it a buffer... with some...
0
by: metaperl | last post by:
A Comparison of Python Class Objects and Init Files for Program Configuration ============================================================================= Terrence Brannon bauhaus@metaperl.com...
0
by: SvenMathijssen | last post by:
Hi, I've been wrestling with a problem for some time that ought to be fairly simple, but turns out to be very difficult for me to solve. Maybe someone here knows the answer. What I try to do is...
16
by: Neil | last post by:
I posted a few days ago that it seems to me that the Access 2007 rich text feature does not support: a) full text justification; b) programmatic manipulation. I was hoping that someone might...
4
by: Stef Mientki | last post by:
hello, I had a program that worked perfectly well. In this program modules were dynamically added, just by putting the file in a predefined directory. Now one of the interface mechanisms was...
0
by: Gabriel Genellina | last post by:
En Thu, 10 Jul 2008 17:37:42 -0300, Ethan Furman <ethan@stoneleaf.us> escribi�: If your objects obey the trichotomy law (an object MUST BE less than, greater than, or equal to another one,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.