8

我经常使用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其他方法时才填写。由于 tuple/namedtuple 有这样的方法,total_ordering 没有为我做任何事情。

所以我想我的选择是

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

有关解决此问题的最佳方法的建议?

4

3 回答 3

13

选项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并使用它来代替

于 2012-09-27T05:27:27.153 回答
4

如果正如您的问题所暗示的那样,您的兴趣仅在于通过备用键对命名元组进行排序,那么为什么不将 sort/sortedkey参数与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)]
于 2012-09-27T06:17:31.387 回答
1

我的建议是按照您希望它们排序的顺序使用字段创建您的命名元组。您可能需要更改创建值的代码部分(例如,更改someTuple("name", 24)someTuple(24, "name")编写所有比较方法的麻烦,并且作为奖励还避免了始终调用这些自定义比较方法的额外性能开销。

于 2012-09-27T04:59:41.170 回答