为什么我不能在Python 3中使用方法__cmp__和Python 2一样?

Why can't I use the method __cmp__ in Python 3 as for Python 2?

下面这段代码

1
2
3
4
5
6
7
8
9
10
class point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def dispc(self):
        return ('(' + str(self.x) + ',' + str(self.y) + ')')

    def __cmp__(self, other):
        return ((self.x > other.x) and (self.y > other.y))

在python 2中工作正常,但在python 3中,我得到一个错误:

1
2
3
4
5
6
>>> p=point(2,3)
>>> q=point(3,4)
>>> p>q
Traceback (most recent call last):
  File"<stdin>", line 1, in <module>
TypeError: unorderable types: point() > point()

它只适用于==!=


在python 3中需要提供丰富的排序比较方法,分别是__lt____gt____le____ge____eq____ne__。另请参见:PEP 207——丰富的比较。

不再使用__cmp__

更具体地说,__lt__selfother为论据,需要返回self是否小于other。例如:

1
2
3
4
class Point(object):
    ...
    def __lt__(self, other):
        return ((self.x < other.x) and (self.y < other.y))

(这不是一个明智的比较实现,但是很难知道您要做什么。)

因此,如果您有以下情况:

1
2
3
4
p1 = Point(1, 2)
p2 = Point(3, 4)

p1 < p2

这相当于:

1
p1.__lt__(p2)

将返回True

如果积分相等,__eq__将返回True,否则返回False。其他方法的工作原理类似。

如果使用functools.total_ordering修饰器,只需实现__lt____eq__方法:

1
2
3
4
5
6
7
8
9
from functools import total_ordering

@total_ordering
class Point(object):
    def __lt__(self, other):
        ...

    def __eq__(self, other):
        ...


这是python 3中一个重大的、经过深思熟虑的变化。有关详细信息,请参阅此处。

  • The ordering comparison operators (<, <=, >=, >) raise a TypeError exception when the operands don’t have a meaningful natural ordering. Thus, expressions like 1 < '', 0 > None or len <= len are no longer valid, and e.g. None < None raises TypeError instead of returning False. A corollary is that sorting a heterogeneous list no longer makes sense – all the elements must be comparable to each other. Note that this does not apply to the == and != operators: objects of different incomparable types always compare unequal to each other.
  • builtin.sorted() and list.sort() no longer accept the cmp argument providing a comparison function. Use the key argument instead. N.B. the key and reverse arguments are now"keyword-only".
  • The cmp() function should be treated as gone, and the __cmp__() special method is no longer supported. Use __lt__() for sorting, __eq__() with __hash__(), and other rich comparisons as needed. (If you really need the cmp() functionality, you could use the expression (a > b) - (a < b) as the equivalent for cmp(a, b).)


在python3中,六个丰富的比较运算符

1
2
3
4
5
6
__lt__(self, other)
__le__(self, other)
__eq__(self, other)
__ne__(self, other)
__gt__(self, other)
__ge__(self, other)

必须单独提供。这可以通过使用functools.total_ordering来缩写。

然而,大多数情况下,这是不可读和不实际的。不过,您仍然需要在2个函数中放入类似的代码片段——或者使用另一个helper func。

所以我更喜欢使用下面显示的mixin类PY3__cmp__。这就重新建立了单一的__cmp__方法框架,在大多数情况下,它是非常清晰和实用的。仍可以覆盖选定的丰富比较。

您的示例将变成:

1
2
3
 class point(PY3__cmp__):
      ...
      # unchanged code

。Py3_uuu cmp_uuuuuu混音类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PY3 = sys.version_info[0] >= 3
if PY3:
    def cmp(a, b):
        return (a > b) - (a < b)
    # mixin class for Python3 supporting __cmp__
    class PY3__cmp__:  
        def __eq__(self, other):
            return self.__cmp__(other) == 0
        def __ne__(self, other):
            return self.__cmp__(other) != 0
        def __gt__(self, other):
            return self.__cmp__(other) > 0
        def __lt__(self, other):
            return self.__cmp__(other) < 0
        def __ge__(self, other):
            return self.__cmp__(other) >= 0
        def __le__(self, other):
            return self.__cmp__(other) <= 0
else:
    class PY3__cmp__:
        pass