【问题标题】:Custom sorting on a namedtuple class对命名元组类的自定义排序
【发布时间】:2012-09-27 04:42:33
【问题描述】:

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

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

【问题讨论】:

  • 你为什么不按照你想要排序的顺序创建namedtuple?
  • 直到我创建并使用了一段时间后,我才意识到我想要对它进行排序/最大化等。所以我现在可以添加一个前导字段作为排序字段,但这可能会有点破坏性。
  • 但是你是如何使用namedtuple的呢? namedtuple 的好处是它允许您按名称访问项目,因此您可以更改您的 namedtuple 以使字段按正确的顺序排列并且不会影响您的代码,只要您按名称访问字段(大概是做,否则为什么使用namedtuple?)。
  • 是的,在访问字段时,我确实使用了该名称,但在初始化它们时通常不使用(请参阅我在问题#3 处的评论)。

标签: python sorting namedtuple functools


【解决方案1】:

选项 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 与选项 2 的开销是否存在显着差异?
  • +1。选项1是直接解决total_ordering只填充缺失值的问题。 @Will Option 1 的开销几乎为零(方法解析顺序中的一个额外步骤。@JohnLaRooy,我建议在您的 Option1 中添加 __slots__ = () 以恢复内存效率。
【解决方案2】:

如果,正如您的问题所暗示的那样,您的兴趣只在于通过备用键排序命名元组,为什么不将 sort/sorted key 参数与 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)]

【讨论】:

  • 排序字段还不存在,所以我要么需要添加它,要么使用 cmp 方法查看 2 个字段并对它们进行一些逻辑处理。 (我的示例代码过于简化)
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-28
    • 1970-01-01
    • 2011-01-29
    • 1970-01-01
    • 1970-01-01
    • 2015-01-28
    相关资源
    最近更新 更多