Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #52353 > unrolled thread

Elegant compare

Started byJason Friedman <jsf80238@gmail.com>
First post2013-08-10 21:41 -0600
Last post2013-08-11 09:17 -0600
Articles 3 — 2 participants

Back to article view | Back to comp.lang.python


Contents

  Elegant compare Jason Friedman <jsf80238@gmail.com> - 2013-08-10 21:41 -0600
    Re: Elegant compare Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-08-11 08:26 +0000
      Re: Elegant compare Jason Friedman <jsf80238@gmail.com> - 2013-08-11 09:17 -0600

#52353 — Elegant compare

FromJason Friedman <jsf80238@gmail.com>
Date2013-08-10 21:41 -0600
SubjectElegant compare
Message-ID<mailman.460.1376192467.1251.python-list@python.org>
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?

[toc] | [next] | [standalone]


#52367

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-08-11 08:26 +0000
Message-ID<52074ab3$0$30000$c3e8da3$5496439d@news.astraweb.com>
In reply to#52353
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

[toc] | [prev] | [next] | [standalone]


#52393

FromJason Friedman <jsf80238@gmail.com>
Date2013-08-11 09:17 -0600
Message-ID<mailman.486.1376236134.1251.python-list@python.org>
In reply to#52367
> 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.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web