在Python中,如何使用自定义键与多个值进行比较?

时间:2012-05-08 16:09:34

标签: python

在实现类的__eq____lt__方法时,通常的做法是使用元组对要比较的值进行分组,如下所示:

@total_ordering
class Foo(object):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    def __hash__(self):
        return hash((self.c, self.a, self.b))

    def __eq__(self, other):
        return (self.c, self.a, self.b) == (other.c, other.a, other.b)

    def __lt__(self, other):
        return (self.c, self.a, self.b) < (other.c, other.a, other.b)

然而,这使用每个键的自然顺序。如果我想更改,例如a如何排序?

,该怎么办?

这是我到目前为止所提出的,虽然看起来工作正常,但我想知道是否有更好的方法可以解决这个问题:

@total_ordering
class Foo(object):
    def __init__(self, a, b, c):
        self.a = MyA(a) # Note
        self.b = b
        self.c = c

    def __hash__(self):
        return hash((self.c, self.a, self.b))

    def __eq__(self, other):
        return (self.c, self.a, self.b) == (other.c, other.a, other.b)

    def __lt__(self, other):
        return (self.c, self.a, self.b) < (other.c, other.a, other.b)

class MyA(A):
    def __hash__(self):
        # ...

    def __eq__(self, other):
        # ...

    def __lt__(self, other):
        # ...

子类化A让我可以定义我的自定义排序,并允许MyA以其他方式表现得像常规A这样很好,但它似乎很浪费/不必要地冗长,特别是如果我必须为多个领域这样做。

编辑:根据用户1320237的回答,这就是我的想法:

@total_ordering
class Foo(object):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    def __hash__(self):
        return hash((self.c, self.a, self.b))

    def __eq__(self, other):
        return (0, 0, 0) == (cmp(self.c, other.c),
                             cmpA(self.a, other.a),
                             cmp(self.b, other.b))

    def __lt__(self, other):
        return (0, 0, 0) > (cmp(self.c, other.c),
                            cmpA(self.a, other.a),
                            cmp(self.b, other.b))

def cmpA(a1, a2):
    # ...

(注意>中的__lt__ cmp(x, y)-1如果x < y__lt__返回True则返回{{1}}} < / p>

2 个答案:

答案 0 :(得分:2)

如果你想用list.sort()订购,例如你可以传递参数:

您的代码:

...
    def __lt__(self, other):
        return (self.c, self.a, self.b) < (other.c, other.a, other.b)

...
list.sort()

当量:

list.sort(key = lambda self: (self.c, self.a, self.b))

也等同于:

list.sort(cmp = lambda self, other: \
                (self.c, self.a, self.b) < (other.c, other.a, other.b))

所以,如果你想以不同的方式对你的答案进行排序,我建议:

class Foo(object):
    @staticmethod
    def cmp_absoluteOrder(self, other):
        return (self.c, self.a, self.b) < (other.c, other.a, other.b)

    @staticmethod
    def cmp_otherOrder(self, other):
        return ...

    @staticmethod
    def cmp_combinedSort(cmpA, cmpB, cmpC):
        return lambda self, other: (0, 0, 0) < (cmpA(self.c, other.c), cmpA(self.a, other.a), cmpA(self.b, other.b), )

    def __hash__(self):
        return hash(self.c) ^ hashA(self.a) ^ hash(self.b)

...
list.sort(cmp = Foo.cmp_absoluteSorting)
list.sort(cmp = Foo.cmp_combinedSort(cmp, (lambda a1, a2: ...), cmp))

hashA = hash # or replace it if important # but the same a will retunrn the same hash

或类似的东西

答案 1 :(得分:1)

如果这只是一次性动作,可以使用以下内容:

def custom_sorter(foo):
   """Takes Foo objects and gives a representation for sorting."""
   return (foo.c, A(foo.a), foo.b)

sorted(foo_list, key=custom_sorter)

如果要多次完成,您可以考虑在Foo上使用可扩展的类方法并吐出类似于custom_sorter的原型。

如果您多次使用相同自定义排序器,为什么不将它作为课程的一部分呢?

你真的只需要问自己为什么我真的需要