Question about sorted in Python 3.0rc1

Discussion in 'Python' started by josh logan, Sep 22, 2008.

  1. josh logan

    josh logan Guest

    Hello,


    I have 2 questions. Say I have this class:

    class Player(object):
    def __init__(self, fname, lname, score):
    self.score = score
    self.fname = fname
    self.lname = lname
    def __cmp__(self, other):
    return (-cmp(self.score, other.score) or
    cmp(self.lname, other.lname) or
    cmp(self.fname, other.fname))
    def __repr__(self):
    return 'Player(fname={0.fname}, lname={0.lname},
    score={0.score})'.format(self)
    def __eq__(self, others):
    if isinstance(other, Player):
    return (self.score == other.score and
    self.lname == other.lname and
    self.fname == other.fname)
    return False
    def __ne__(self, others):
    return not self.__eq__(others)



    fnames = ['Julie', 'Ben', 'Jason', 'David']
    lnames = ['Parks', 'Smith']
    scores = [100, 95, 95, 130, 58, 74]

    import itertools as it

    score_iter = it.cycle(scores)

    P = [Player(fn, ln, next(score_iter)) for fn in fnames for ln in
    lnames]

    cmp(P[0], P[1]) # returns -1

    sorted(P) # throws TypeError: unorderable types Player() < Player()

    The sorted function works when I define __lt__.
    I must be misreading the documentation, because I read for the
    documentation __cmp__ that it is called if none of the other rich
    comparison functions are defined.
    Is this a bug in Python 3.0rc1, or am I missing something?


    Secondly, say that we suddenly need another sorting order, where we
    want to sort by decreasing score and then by DECREASING last name
    (instead of increasing last name, defined above). Now that the
    comparison function argument is taken away from the sorted builtin,
    how do we accomplish this with the "key" parameter?

    Thank you
    josh logan, Sep 22, 2008
    #1
    1. Advertising

  2. On 22 Sep, 04:05, josh logan <> wrote:
    > Hello,
    >
    > I have 2 questions. Say I have this class:
    >
    > class Player(object):
    > def __init__(self, fname, lname, score):
    > self.score = score
    > self.fname = fname
    > self.lname = lname
    > def __cmp__(self, other):
    > return (-cmp(self.score, other.score) or
    > cmp(self.lname, other.lname) or
    > cmp(self.fname, other.fname))
    > def __repr__(self):
    > return 'Player(fname={0.fname}, lname={0.lname},
    > score={0.score})'.format(self)
    > def __eq__(self, others):
    > if isinstance(other, Player):
    > return (self.score == other.score and
    > self.lname == other.lname and
    > self.fname == other.fname)
    > return False
    > def __ne__(self, others):
    > return not self.__eq__(others)
    >
    > fnames = ['Julie', 'Ben', 'Jason', 'David']
    > lnames = ['Parks', 'Smith']
    > scores = [100, 95, 95, 130, 58, 74]
    >
    > import itertools as it
    >
    > score_iter = it.cycle(scores)
    >
    > P = [Player(fn, ln, next(score_iter)) for fn in fnames for ln in
    > lnames]
    >
    > cmp(P[0], P[1]) # returns -1
    >
    > sorted(P) # throws TypeError: unorderable types Player() < Player()
    >
    > The sorted function works when I define __lt__.
    > I must be misreading the documentation, because I read for the
    > documentation __cmp__ that it is called if none of the other rich
    > comparison functions are defined.
    > Is this a bug in Python 3.0rc1, or am I missing something?
    >
    > Secondly, say that we suddenly need another sorting order, where we
    > want to sort by decreasing score and then by DECREASING last name
    > (instead of increasing last name, defined above). Now that the
    > comparison function argument is taken away from the sorted builtin,
    > how do we accomplish this with the "key" parameter?
    >
    > Thank you


    I don't know about __cmp__ but for the second part of the question you
    can probably do:

    sorted(P, key=lambda p: (p.score, p.lname), reverse=True)

    HTH

    --
    Arnaud
    Arnaud Delobelle, Sep 22, 2008
    #2
    1. Advertising

  3. josh logan

    josh logan Guest

    On Sep 22, 3:41 am, Arnaud Delobelle <> wrote:
    > On 22 Sep, 04:05, josh logan <> wrote:
    >
    >
    >
    > > Hello,

    >
    > > I have 2 questions. Say I have this class:

    >
    > > class Player(object):
    > >     def __init__(self, fname, lname, score):
    > >         self.score = score
    > >         self.fname = fname
    > >         self.lname = lname
    > >     def __cmp__(self, other):
    > >         return (-cmp(self.score, other.score) or
    > >                 cmp(self.lname, other.lname) or
    > >                 cmp(self.fname, other.fname))
    > >     def __repr__(self):
    > >         return 'Player(fname={0.fname}, lname={0.lname},
    > > score={0.score})'.format(self)
    > >     def __eq__(self, others):
    > >         if isinstance(other, Player):
    > >             return (self.score == other.score and
    > >                     self.lname == other.lname and
    > >                     self.fname == other.fname)
    > >         return False
    > >     def __ne__(self, others):
    > >         return not self.__eq__(others)

    >
    > > fnames = ['Julie', 'Ben', 'Jason', 'David']
    > > lnames = ['Parks', 'Smith']
    > > scores = [100, 95, 95, 130, 58, 74]

    >
    > > import itertools as it

    >
    > > score_iter = it.cycle(scores)

    >
    > > P = [Player(fn, ln, next(score_iter)) for fn in fnames for ln in
    > > lnames]

    >
    > > cmp(P[0], P[1]) # returns -1

    >
    > > sorted(P) # throws TypeError: unorderable types Player() < Player()

    >
    > > The sorted function works when I define __lt__.
    > > I must be misreading the documentation, because I read for the
    > > documentation __cmp__ that it is called if none of the other rich
    > > comparison functions are defined.
    > > Is this a bug in Python 3.0rc1, or am I missing something?

    >
    > > Secondly, say that we suddenly need another sorting order, where we
    > > want to sort by decreasing score and then by DECREASING last name
    > > (instead of increasing last name, defined above). Now that the
    > > comparison function argument is taken away from the sorted builtin,
    > > how do we accomplish this with the "key" parameter?

    >
    > > Thank you

    >
    > I don't know about __cmp__ but for the second part of the question you
    > can probably do:
    >
    >     sorted(P, key=lambda p: (p.score, p.lname), reverse=True)
    >
    > HTH
    >
    > --
    > Arnaud


    Thank you for the prompt reply. I didn't think my second question
    completely through.

    A better example would be sorting by increasing last name and
    decreasing first name. This would be easy with the sort function
    comparator, but I can't see how to do the same with the key argument.
    Is the only solution to decorate the Player objects in another class
    that has the appropriate __cmp__ function (or whatever is needed) and
    then retrieve the Player objects back?
    josh logan, Sep 22, 2008
    #3
  4. josh logan

    Peter Otten Guest

    josh logan wrote:

    > A better example would be sorting by increasing last name and
    > decreasing first name. This would be easy with the sort function
    > comparator, but I can't see how to do the same with the key argument.
    > Is the only solution to decorate the Player objects in another class
    > that has the appropriate __cmp__ function (or whatever is needed) and
    > then retrieve the Player objects back?


    Python's sort algorithm is guaranteed to be stable; therefore you can sort
    twice:

    ordered = sorted(players, key=lambda p: p.fname, reverse=True)
    ordered.sort(key=lambda p: p.lname)

    Peter
    Peter Otten, Sep 22, 2008
    #4
  5. josh logan <> wrote:
    >sorted(P) # throws TypeError: unorderable types Player() < Player()
    >
    >The sorted function works when I define __lt__.
    >I must be misreading the documentation, because I read for the
    >documentation __cmp__ that it is called if none of the other rich
    >comparison functions are defined.


    You're either misreading or forgetting that __eq__ and __ne__,
    which you define, are rich comparison functions. __cmp__ will only
    be called for a comparison when *none* of the rich comparison
    functions are defined, not just the one in question.

    --
    \S -- -- http://www.chaos.org.uk/~sion/
    "Frankly I have no feelings towards penguins one way or the other"
    -- Arthur C. Clarke
    her nu becomeþ se bera eadward ofdun hlæddre heafdes bæce bump bump bump
    Sion Arrowsmith, Sep 22, 2008
    #5
  6. On 22 Sep, 11:52, josh logan <> wrote:
    > On Sep 22, 3:41 am, Arnaud Delobelle <> wrote:
    >
    >
    >
    > > On 22 Sep, 04:05, josh logan <> wrote:

    >
    > > > Hello,

    >
    > > > I have 2 questions. Say I have this class:

    >
    > > > class Player(object):
    > > > def __init__(self, fname, lname, score):
    > > > self.score = score
    > > > self.fname = fname
    > > > self.lname = lname
    > > > def __cmp__(self, other):
    > > > return (-cmp(self.score, other.score) or
    > > > cmp(self.lname, other.lname) or
    > > > cmp(self.fname, other.fname))
    > > > def __repr__(self):
    > > > return 'Player(fname={0.fname}, lname={0.lname},
    > > > score={0.score})'.format(self)
    > > > def __eq__(self, others):
    > > > if isinstance(other, Player):
    > > > return (self.score == other.score and
    > > > self.lname == other.lname and
    > > > self.fname == other.fname)
    > > > return False
    > > > def __ne__(self, others):
    > > > return not self.__eq__(others)

    >
    > > > fnames = ['Julie', 'Ben', 'Jason', 'David']
    > > > lnames = ['Parks', 'Smith']
    > > > scores = [100, 95, 95, 130, 58, 74]

    >
    > > > import itertools as it

    >
    > > > score_iter = it.cycle(scores)

    >
    > > > P = [Player(fn, ln, next(score_iter)) for fn in fnames for ln in
    > > > lnames]

    >
    > > > cmp(P[0], P[1]) # returns -1

    >
    > > > sorted(P) # throws TypeError: unorderable types Player() < Player()

    >
    > > > The sorted function works when I define __lt__.
    > > > I must be misreading the documentation, because I read for the
    > > > documentation __cmp__ that it is called if none of the other rich
    > > > comparison functions are defined.
    > > > Is this a bug in Python 3.0rc1, or am I missing something?

    >
    > > > Secondly, say that we suddenly need another sorting order, where we
    > > > want to sort by decreasing score and then by DECREASING last name
    > > > (instead of increasing last name, defined above). Now that the
    > > > comparison function argument is taken away from the sorted builtin,
    > > > how do we accomplish this with the "key" parameter?

    >
    > > > Thank you

    >
    > > I don't know about __cmp__ but for the second part of the question you
    > > can probably do:

    >
    > > sorted(P, key=lambda p: (p.score, p.lname), reverse=True)

    >
    > > HTH

    >
    > > --
    > > Arnaud

    >
    > Thank you for the prompt reply. I didn't think my second question
    > completely through.
    >
    > A better example would be sorting by increasing last name and
    > decreasing first name. This would be easy with the sort function
    > comparator, but I can't see how to do the same with the key argument.
    > Is the only solution to decorate the Player objects in another class
    > that has the appropriate __cmp__ function (or whatever is needed) and
    > then retrieve the Player objects back?


    You can use the stability of the sorting algorithm as Peter pointed
    out. If decorating, you don't need to decorate the Player objects,
    but you can decorate the keys instead: e.g

    class RevStr(str):
    def __lt__(self, other): return str.__lt__(other, self)
    def __ge__(self, other): retrn str.__gt__(other, self)

    then you can write:

    sorted(P, key=lambda p:(p.lname, RevStr(p.fname)))

    --
    Arnaud
    Arnaud Delobelle, Sep 22, 2008
    #6
  7. josh logan

    josh logan Guest

    On Sep 22, 7:32 am, Sion Arrowsmith <>
    wrote:
    > josh logan  <> wrote:
    >
    > >sorted(P) # throws TypeError: unorderable types Player() < Player()

    >
    > >The sorted function works when I define __lt__.
    > >I must be misreading the documentation, because I read for the
    > >documentation __cmp__ that it is called if none of the other rich
    > >comparison functions are defined.

    >
    > You're either misreading or forgetting that __eq__ and __ne__,
    > which you define, are rich comparison functions. __cmp__ will only
    > be called for a comparison when *none* of the rich comparison
    > functions are defined, not just the one in question.
    >
    > --
    > \S -- --http://www.chaos.org.uk/~sion/
    >    "Frankly I have no feelings towards penguins one way or the other"
    >         -- Arthur C. Clarke
    >    her nu becomeþ se bera eadward ofdun hlæddre heafdes bæce bump bump bump


    Hello Sion,

    When I don't define the __eq__ and __ne__ comparison functions, the
    same unexpected behavior occurs.
    josh logan, Sep 22, 2008
    #7
  8. josh logan

    josh logan Guest

    On Sep 22, 9:29 am, Hrvoje Niksic <> wrote:
    > josh logan <> writes:
    > > sorted(P) # throws TypeError: unorderable types Player() < Player()

    >
    > > The sorted function works when I define __lt__.
    > > I must be misreading the documentation, because I read for the
    > > documentation __cmp__ that it is called if none of the other rich
    > > comparison functions are defined.
    > > Is this a bug in Python 3.0rc1, or am I missing something?

    >
    > What documentation are you referring to, exactly?  The whole __cmp__
    > thing was supposed to be removed from Python 3, so mention of it
    > sounds like a documentation bug.
    >
    > > Secondly, say that we suddenly need another sorting order, where we
    > > want to sort by decreasing score and then by DECREASING last name
    > > (instead of increasing last name, defined above). Now that the
    > > comparison function argument is taken away from the sorted builtin,
    > > how do we accomplish this with the "key" parameter?

    >
    > By calling sort twice on the sequence:
    >
    > lst.sort(key=lambda x: x.score)
    > lst.sort(key=lambda x: x.last_name, reverse=True)
    >
    > As much as I like the key argument, I believe it was a mistake to
    > remove cmp, simply because it was the more general mechanism.
    > Emulating cmp with key is possible, but it requires creating a bunch
    > of objects for each sort.  (I'm aware that list.sort caches the
    > calculated keys, but it still has to create as many of them as there
    > are list items.)


    It looks like __cmp__ is still in the documentation, and it seems to
    work somewhat in Python 3.0rc1. Here is the link to the documnetation
    http://docs.python.org/dev/3.0/reference/datamodel.html#object.__cmp__
    josh logan, Sep 22, 2008
    #8
  9. josh logan

    Terry Reedy Guest

    josh logan wrote:

    Here is a minimal example showing the problematic behavior.

    class Int():
    def __init__(self, i):
    self.i = i
    def __cmp__(self, other):
    return cmp(self.i, other.i)

    Is = [Int(i) for i in range(8)]
    Is.sort() # throws TypeError: unorderable types Int() < Int()
    sorted(Is) # ditto, if above not present

    The 3.0b2 version of LibRef/ Built-in Types/ Comparisions says
    "Instances of a class cannot be ordered with respect to other instances
    of the same class, or other types of object, unless the class defines
    enough of the methods __cmp__(), __lt__(), __le__(), __gt__(), and
    __ge__() (in general, either __cmp__() or both __lt__() and __eq__() are
    sufficient, if you want the conventional meanings of the comparison
    operators).

    The notes for Mutable Sequence .sort() say nothing more. So the
    exception appears to be a bug, perhaps left over from when there was a
    plan (since aborted) to delete cmp and __cmp__. If the 3.0c1 version of
    the docs say the same, and no one says otherwise, I would file a report
    on the tracker at bugs.python.org, using the minimal example above.

    Terry Jan Reedy
    Terry Reedy, Sep 22, 2008
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Michael Sweet

    [ANNOUNCMENT] Mini-XML 2.0rc1

    Michael Sweet, May 21, 2004, in forum: XML
    Replies:
    0
    Views:
    354
    Michael Sweet
    May 21, 2004
  2. Alexander DEJANOVSKI

    JyRetic EAI Server 1.0RC1 released

    Alexander DEJANOVSKI, Jan 13, 2004, in forum: Python
    Replies:
    0
    Views:
    257
    Alexander DEJANOVSKI
    Jan 13, 2004
  3. Alexander DEJANOVSKI

    JyRetic EAI Server 1.0RC1 released

    Alexander DEJANOVSKI, Jan 14, 2004, in forum: Python
    Replies:
    0
    Views:
    314
    Alexander DEJANOVSKI
    Jan 14, 2004
  4. Barry Warsaw

    RELEASED Python 2.6rc2 and 3.0rc1

    Barry Warsaw, Sep 18, 2008, in forum: Python
    Replies:
    0
    Views:
    254
    Barry Warsaw
    Sep 18, 2008
  5. Replies:
    1
    Views:
    232
    Martin v. Löwis
    Oct 14, 2008
Loading...

Share This Page