Re: Elegant compare

2013-08-11 Thread Steven D'Aprano
On Sat, 10 Aug 2013 21:41:00 -0600, Jason Friedman wrote:

 class my_class:
 def __init__(self, attr1, attr2):
 self.attr1 = attr1 #string
 self.attr2 = attr2 #string
 def __lt__(self, other):
 if self.attr1  other.attr1:
 return True
 else:
 return self.attr2  other.attr2
 
 I will run into problems if attr1 or attr2 is None, and they
 legitimately can be.
 
 I know I can check for attr1 or attr2 or both being None and react
 accordingly, but my real class has ten attributes and that approach will
 be long.  What are my alternatives?

This is a hard question to answer, because your code snippet isn't 
clearly extensible to the case where you have ten attributes. What's the 
rule for combining them? If instance A has five attributes less than 
those of instance B, and five attributes greater than those of instance 
B, which wins?

But if I had to guess an approach, I'd start with a helper function (or 
method) that compares two raw values:

def compare(a, b):
Return -ve for less than, 0 for equal, +ve for greater than.
if a is None:
 return 0 if b is None else -1
if b is None:
 return 1
return (b  a) - (a  b)


Now, in your class, you can use this helper function to check each 
attribute in turn. Assuming that if an attribute is equal, you move on to 
check the next one:

class MyClass:
def _compare(self, other):
for attr in 'attr1 attr2 attr3 attr4'.split():
a, b = getattr(self, attr), getattr(other, attr)
triflag = compare(a, b)
if triflag:
return triflag
return 0
def __lt__(self, other):
if not isinstance(other, MyClass):
return NotImplemented
return self._compare(other)  0
def __eq__(self, other):
if not isinstance(other, MyClass):
return NotImplemented
return not self._compare(other)
def __ne__(self, other):
if not isinstance(other, MyClass):
return NotImplemented
return bool(self._compare(other))

and so on. You can save a certain amount of repetition (by my count, six 
lines of code) by pulling out the if not isinstance check into a 
decorator, but since the decorator is likely to be about six lines long, 
I wouldn't bother :-)



-- 
Steven
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Elegant compare

2013-08-11 Thread Jason Friedman
 This is a hard question to answer, because your code snippet isn't
 clearly extensible to the case where you have ten attributes. What's the
 rule for combining them? If instance A has five attributes less than
 those of instance B, and five attributes greater than those of instance
 B, which wins?

Yes, my code snippet was too short, I should have said:

class my_class:
def __init__(self, attr1, attr2, attr3):
self.attr1 = attr1 #string
self.attr2 = attr2 #string
self.attr3 = attr3 #string
def __lt__(self, other):
if self.attr1  other.attr1:
return True
elif self.attr2  other.attr2:
return True
else:
return self.attr3  other.attr3

Chris's answer is actually perfectly adequate for my needs.
Thank you Steve and Chris.
-- 
http://mail.python.org/mailman/listinfo/python-list


Elegant compare

2013-08-10 Thread Jason Friedman
class my_class:
def __init__(self, attr1, attr2):
self.attr1 = attr1 #string
self.attr2 = attr2 #string
def __lt__(self, other):
if self.attr1  other.attr1:
return True
else:
return self.attr2  other.attr2

I will run into problems if attr1 or attr2 is None, and they
legitimately can be.

I know I can check for attr1 or attr2 or both being None and react
accordingly, but my real class has ten attributes and that approach
will be long.  What are my alternatives?
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Elegant compare

2013-08-10 Thread Chris Angelico
On Sun, Aug 11, 2013 at 4:41 AM, Jason Friedman jsf80...@gmail.com wrote:
 class my_class:
 def __init__(self, attr1, attr2):
 self.attr1 = attr1 #string
 self.attr2 = attr2 #string
 def __lt__(self, other):
 if self.attr1  other.attr1:
 return True
 else:
 return self.attr2  other.attr2

 I will run into problems if attr1 or attr2 is None, and they
 legitimately can be.

 I know I can check for attr1 or attr2 or both being None and react
 accordingly, but my real class has ten attributes and that approach
 will be long.  What are my alternatives?

The first question is: What should the comparison do with a None
value? Should it be considered less than every string? If so, you
could simply use:

if (self.attr1 or )  (other.attr1 or ):

which will treat any falsy value as blank.

ChrisA
-- 
http://mail.python.org/mailman/listinfo/python-list