top button
Flag Notify
Site Registration

Elegant compare in python

0 votes
225 views
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?

posted Aug 11, 2013 by Sheetal Chauhan

Share this question
Facebook Share Button Twitter Share Button LinkedIn Share Button

2 Answers

+1 vote

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 false value as blank.

answer Aug 11, 2013 by Mandeep Sehgal
+1 vote

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 is instance" check into a decorator, but since the decorator is likely to be about six lines long, I wouldn't bother :-)

answer Aug 11, 2013 by Deepak Dasgupta
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

your answer is actually perfectly adequate for my needs.
...