Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom sorting on a namedtuple class

I use namedtuple classes a lot. I have been thinking today if there is a nice way to implement custom sorting for such a class, i.e. make the default sort key not the first element (then second, third, etc) of the namedtuple.

My first instinct was to implement __lt__ and __eq__ and let total_ordering do the rest (it fills out le, ne, gt, ge):

from collections import namedtuple
from functools import total_ordering


@total_ordering
class B(namedtuple('B', 'x y')):
    def __lt__(self, other):
        return self.y < other.y

However:

def test_sortingB():
    b1 = B(1, 2)
    b2 = B(2, 1)
    assert b2 < b1  # passes
    assert b2 <= b1  # fails

oh, right... total_ordering only fills out the other methods if they are missing. Since tuple/namedtuple has such methods, total_ordering isn't doing anything for me.

So I guess my options are

  1. stop using namedtuple and just build my own boring class, keep using total_ordering
  2. keep using namedtuple and implement all 6 comparison methods
  3. keep using namedtuple and insert a sort value as the first field. Luckily I don't have too many instances of the class, but usually I just rely on the order of the fields to initialise them which could be nasty. Maybe that is a bad habit.

Suggestions on the best way to solve this?

like image 654
pfctdayelise Avatar asked Sep 27 '12 04:09

pfctdayelise


1 Answers

OPTION 1. Use a mixin and apply the total_ordering to that

@total_ordering
class B_ordering(object):
    __slots__ = ()                 # see Raymond's comment
    def __lt__(self, other):
        return self.y < other.y

class B(B_ordering, namedtuple('B', 'x y')):
    pass

OPTION 2. Make your own decorator based on total_ordering and just use that instead

like image 142
John La Rooy Avatar answered Sep 24 '22 02:09

John La Rooy