对命名元组类进行自定义排序



我经常使用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并插入一个排序值作为第一个字段。幸运的是,我没有太多的类实例,但通常我只是依靠字段的顺序来初始化它们,这可能很糟糕。也许这是个坏习惯

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

选项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制作自己的装饰器,然后使用它来代替

如果正如您的问题所暗示的那样,您只对按备用键对命名元组进行排序感兴趣,为什么不将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)]

我的建议是按照您希望的字段排序顺序创建名称元组。您可能需要更改代码中创建值的部分(例如,将someTuple("name", 24)更改为someTuple(24, "name"),但通常情况下,值创建的位置比使用的位置少,所以这应该不会太大。这避免了编写所有比较方法的麻烦,而且作为奖励,还避免了一直调用这些自定义比较方法所带来的额外性能开销。

相关内容

  • 没有找到相关文章

最新更新