在namedtuple类上自定义排序

时间:2012-09-27 04:42:33

标签: python sorting namedtuple functools

我经常使用namedtuple个课程。我一直在想今天是否有一种很好的方法来为这样的类实现自定义排序,即使默认排序键不是namedtuple的第一个元素(然后是第二个,第三个等)。

我的第一直觉是实施__lt____eq__,让total_ordering完成剩下的工作(它填写了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

然而:

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

哦,对...... total_ordering只填写其他方法if they are missing。由于tuple / namedtuple有这样的方法,total_ordering对我没有任何作用。

所以我想我的选择是

  1. 停止使用namedtuple并构建我自己的无聊类,继续使用total_ordering
  2. 继续使用namedtuple并实现所有6种比较方法
  3. 继续使用namedtuple并插入排序值作为第一个字段。幸运的是,我没有太多的课程实例,但通常我只是依靠字段的顺序来初始化它们,这可能是令人讨厌的。也许这是一个坏习惯。
  4. 有关解决此问题的最佳方法的建议?

3 个答案:

答案 0 :(得分:11)

选项1.使用mixin并将total_ordering应用于

@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创建自己的装饰器,然后使用

答案 1 :(得分:3)

如果,正如您的问题所暗示的那样,您的兴趣仅在于通过备用键在排序命名元组中,为什么不在key函数中使用sort / sorted attrgetter参数:

>>> 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)]

您可以更进一步,定义自己的排序功能:

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

答案 2 :(得分:1)

我的建议是使用您希望对它们进行排序的顺序创建您的namedtuple。您可能必须更改代码中创建值的部分(例如,将someTuple("name", 24)更改为someTuple(24, "name"),但通常会在比使用的地方更少的位置创建值,因此这不应该'这是一个太大的交易。这避免了编写所有比较方法的麻烦,并且作为奖励也避免了一直调用这些自定义比较方法的额外性能开销。