473,769 Members | 2,062 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

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(se lf, 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 1597
Steven D'Aprano <st***@REMOVE.T HIS.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.plumag e(), 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(se lf, 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(se lf, 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.goo glegroups.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(se lf, 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.r r._bogus_.comwr ote:
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.TH IS.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(se lf, 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):
"""Override n by derived classes to define a comparison key."""
raise NotImplementedE rror()

class Parrot(Comparab le):
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 CompareWithIden tity:
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.2077150344848 6328, 0.1639640331268 3105, 0.1650719642639 1602]
>>timeit.Timer( "x = y", "from __main__ import x,y").repeat()
[0.2091810703277 5879, 0.1618781089782 7148, 0.1635179519653 3203]

And with the identity test:
>>x = CompareWithIden tity(1); y = CompareWithIden tity(1)
timeit.Timer( "x = x", "from __main__ import x,y").repeat()
[0.2076179981231 6895, 0.1690709590911 8652, 0.1642060279846 1914]
>>timeit.Timer( "x = y", "from __main__ import x,y").repeat()
[0.2090909481048 584, 0.1968839168548 584, 0.1647920608520 5078]

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.T HIS.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.T HIS.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 CompareWithIden tity:
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.2077150344848 6328, 0.1639640331268 3105, 0.1650719642639 1602]
>>>timeit.Timer ("x = y", "from __main__ import x,y").repeat()
[0.2091810703277 5879, 0.1618781089782 7148, 0.1635179519653 3203]

And with the identity test:
>>>x = CompareWithIden tity(1); y = CompareWithIden tity(1)
timeit.Timer ("x = x", "from __main__ import x,y").repeat()
[0.2076179981231 6895, 0.1690709590911 8652, 0.1642060279846 1914]
>>>timeit.Timer ("x = y", "from __main__ import x,y").repeat()
[0.2090909481048 584, 0.1968839168548 584, 0.1647920608520 5078]

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 = CompareWithIden tity(1); y = CompareWithIden tity(1)
timeit.Timer( "x == y", "from __main__ import x,y").repeat()
[2.2971229553222 656, 2.2821698188781 738, 2.2767620086669 922]
>>timeit.Timer( "x == x", "from __main__ import x,y").repeat()
[1.6935880184173 584, 1.6783449649810 791, 1.6613109111785 889]
>>x = Compare(1); y = Compare(1)
timeit.Timer( "x == y", "from __main__ import x,y").repeat()
[2.1717329025268 555, 2.1361908912658 691, 2.1338419914245 605]

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

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

Similar topics

5
2067
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 hierarchy would be less than (more specialized) than classes they inherit from. class A(object): pass class B(A):
3
1368
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
1530
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 defined in the Axes class of the axes module. Eg, the Axes class defines a plot method and the matlab interface defines a plot function that gets the current Axes instance, calls the plot method on that instance, and does some error handling. Here...
4
2274
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 values and separators class { private string globalvar = ""; private void manipulate (whattodo ) //substring, join, etc....
0
2509
by: metaperl | last post by:
A Comparison of Python Class Objects and Init Files for Program Configuration ============================================================================= Terrence Brannon bauhaus@metaperl.com http://www.livingcosmos.org/Members/sundevil/python/articles/a-comparison-of-python-class-objects-and-init-files-for-program-configuration/view
0
2581
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 sort the records in a plain-text index file based on certain columns. The index file consists of records and fields within the records. The individual fields are separated by semicolons, the records by newlines. The index file is loaded into memory...
16
11134
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 know one way or the other whether that was true or not, or could point me to an article or help text that would. What I have seen so far online and in Access 2007 help seems to confirm the above. But that (or at least (b)) seems incredible that it...
4
1382
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 to see if some parameter was changed in a an instance, by comparing the value from the instance with its previous value
0
1125
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, and there is no other possibility) then __cmp__ is enough, and easier to define than all the rich comparison operations. In other cases, __cmp__ is not suitable: by example, complex numbers can't define "greater than" ; you can only use "equal" or...
0
9589
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9423
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10215
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, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
10049
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
0
9865
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 protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
1
7410
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
6674
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
5307
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
2
3564
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

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.