Custom sorted by namedtuple class

I am using namedtuple classes. I thought today if there is a good way to implement custom sorting for such a class, i.e. Make the default sort key not the first element (then the second, third, etc.) Namedtuple.

My first instinct was to implement __lt__ and __eq__ and total_ordering to do the rest (it fills 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 

But:

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

oh, right ... total_ordering only populates other methods if they are missing . Since tuple / namedtuple has such methods, total_ordering does nothing for me.

So, I think my options

  • stop using namedtuple and just create your own boring class, keep using total_ordering
  • continue to use namedtuple and implement all 6 comparison methods
  • keep using namedtuple and paste the sort value as the first field. Fortunately, I don't have too many class instances, but usually I just rely on the order of the fields to initialize them, which can be frustrating. Maybe this is a bad habit.

Suggestions on the best way to solve this problem?

+7
source share
3 answers

OPTION 1. Use mixin and apply total_ordering to this

 @total_ordering class B_ordering(object): __slots__ = () # see Raymond 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 it instead

+10
source

If, as your question, your interest is only in sorting namedtuples using an alternate key, why not use the sort / sorted key argument with the attrgetter function:

 >>> from collections import namedtuple >>> from operator import attrgetter >>> P = namedtuple("P", "xy") >>> p1 = P(1, 2) >>> p2 = P(2, 1) >>> sorted([p1, p2], key=attrgetter("y")) [P(x=2, y=1), P(x=1, y=2)] 

You can go even further and define your own sort function:

 >>> from functools import partial >>> sortony = partial(sorted, key=attrgetter("y")) >>> sortony([p1, p2]) [P(x=2, y=1), P(x=1, y=2)] 
+3
source

My advice would be to create your namedtuple with fields in another you want them to be sorted. You may need to change the parts of the code in which you create your values ​​(for example, change someTuple("name", 24) to someTuple(24, "name") ), but usually the values ​​are created in fewer places than they are used, therefore, it should not be too large. This avoids the difficulties of writing all the comparison methods, and also, as a bonus, also avoids the additional overhead of performance associated with the fact that these user matching methods are called all the time.

+1
source

All Articles