python:Python:使用__cmp__对间隔进行排序,其中__lt__表示“严格小于”

Python: sorting Intervals with __cmp__, with __lt__ meaning “strictly less than”

我有一个intervaltree库,需要对IntervalsPoints进行排序。

我有一个__cmp__方法,它强制非常稳定和逻辑排序(代码见末尾)。而且,我有一个有用的__lt__方法来判断间隔是否严格小于彼此(同上)。

我对这种行为做了30次测试,一切都很顺利…

问题

…除非我必须排序:

1
2
>>> sorted([Interval(0, 10), Interval(-10, 5)])
[Interval(0, 10), Interval(-10, 5)]               # WRONG!

因为python使用__lt__,所以它使用"严格小于"的语义而不是__cmp__。我可以强制它显式地这样做,但我宁愿避免繁琐的语法:

1
2
>>> sorted([Interval(0, 10), Interval(-10, 5)], cmp=Interval.__cmp__)
[Interval(-10, 5), Interval(0, 10)]               # RIGHT

在python3中,语法更加繁琐(在python2.6中不可用,在python2.7中可用)。

1
2
>>> from functools import cmp_to_key
>>> sorted([Interval(0, 10), Interval(-10, 5)], key=cmp_to_key(Interval.__cmp__))

我想要什么

我有没有办法让sorted()和朋友自动使用Interval.__cmp__,但仍然保持__lt__不变?也就是说,我想要这种优雅的行为:

1
2
>>> sorted([Interval(0, 10), Interval(-10, 5)])   # no extra arguments!
[Interval(-10, 5), Interval(0, 10)]

附件:__cmp____lt__的实施

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def __cmp__(self, other):
   """
    Tells whether other sorts before, after or equal to this
    Interval.

    Sorting is by begins, then by ends, then by data fields.

    If data fields are not both sortable types, data fields are
    compared alphabetically by type name.
    :param other: Interval
    :return: -1, 0, 1
    :rtype: int
   """

    s = self[0:2]
    try:
        o = other[0:2]
    except:
        o = (other,)
    if s != o:
        return -1 if s < o else 1
    try:
        if self.data == other.data:
            return 0
        return -1 if self.data < other.data else 1
    except TypeError:
        s = type(self.data).__name__
        o = type(other.data).__name__
        if s == o:
            return 0
        return -1 if s < o else 1

def __lt__(self, other):
   """
    Less than operator. Returns False if there is an overlap.
    :param other: Interval or point
    :return: True or False
    :rtype: bool
   """

    return not self.overlaps(other) and self.end <= other

PS:这个问题在开发部门,我想把__lt__的行为改为"严格小于"。在主部门(1.1.0版)中,__lt__只是模仿__cmp__

更新

  • (2014-12):现在在我的开发部门,我提供Interval.sortedInterval.key。不过,仍然不那么优雅。我仍在寻找更好的方法!


我强烈建议不要为您的类定义任何订单运算符。

原因是,顺序运算符的存在意味着顺序关系及其相关语义的存在,而您的类本质上违反了这些语义(间隔上没有定义良好的顺序关系)。

例如,订单关系意味着:

1
not(a<b) and not(b a==b

这对区间来说是不正确的。

换言之,您不应该仅仅决定您希望运算符的含义(例如,__lt__表示严格小于),而是在语言中为您预先定义语义(例如,sorted的实现依赖于该语义)。

您应该做的是定义用于比较的非运算符方法(例如def strict_lt(self, other): ...),并使用它们。我还定义了一个sort_intervals函数,并在代码中直接使用它而不是sorted。例如。:

1
2
def sort_intervals(lst):
    return sorted(lst, cmp = ...)