关于python:在NamedDuple类上自定义排序

Custom sorting on a namedtuple class

我经常使用名称双重类。我今天一直在想,如果有一种很好的方法来实现此类的自定义排序,也就是说,将默认排序键设置为不是nameduple的第一个元素(然后是第二个、第三个等)。

我的第一反应是实施__lt____eq__并让total_ordering做其余的(它填写了le、ne、gt、ge):

1
2
3
4
5
6
7
8
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

然而:

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

哦,对了…total_ordering只在缺少其他方法时填写。因为tuple/namedtuple有这样的方法,所以total-ordering对我没有任何作用。

所以我想我的选择是

  • 停止使用namedtuple,只需创建我自己的无聊类,继续使用total-ordering
  • 继续使用namedtuple并实现所有6种比较方法
  • 继续使用namedtuple并插入排序值作为第一个字段。幸运的是,我没有太多的类实例,但通常我只是依赖字段的顺序来初始化它们,这可能很糟糕。也许这是个坏习惯。
  • 关于解决这一问题的最佳方法的建议?


    选项1。使用一个混音器,并将总订购量应用于

    1
    2
    3
    4
    5
    6
    7
    8
    @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

    选项2。根据total_ordering制作你自己的装饰器,用它代替


    如果,正如您的问题所暗示的那样,您的兴趣仅仅是通过一个备用键对名称进行排序,那么为什么不将sort/sorted key参数与attrgetter函数一起使用:

    1
    2
    3
    4
    5
    6
    7
    >>> from collections import namedtuple
    >>> from operator import attrgetter
    >>> P = namedtuple("P","x y")
    >>> p1 = P(1, 2)
    >>> p2 = P(2, 1)
    >>> sorted([p1, p2], key=attrgetter("y"))
    [P(x=2, y=1), P(x=1, y=2)]

    您可以进一步定义自己的排序函数:

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


    我的建议是按照您希望的排序顺序创建您的名称与字段的副本。您可能需要更改代码中创建值的部分(例如,将someTuple("name", 24)更改为someTuple(24,"name")),但通常创建的值的位置比使用的位置要少,因此这不应该太大。这样就避免了编写所有比较方法的麻烦,而且作为一个额外的好处,还避免了那些定制的比较方法一直被调用的额外性能开销。