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

问题描述 投票:0回答:4

下面一段代码

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 中出现错误:

>>> 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 python-3.x python-2.x partial-ordering
4个回答
102
投票

需要在Python 3中提供丰富的排序比较方法,分别是

__lt__
__gt__
__le__
__ge__
__eq__
 __ne__
。另见:PEP 207——丰富的比较

__cmp__
no 不再使用。


更具体地说,

__lt__
self
other
为参数,需要返回
self
是否小于
other
。例如:

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

(这不是一个明智的比较实现,但很难说出你想要什么。)

所以如果你有以下情况:

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

p1 < p2

这相当于:

p1.__lt__(p2)

这将返回

True

如果点相等,

__eq__
将返回
True
,否则返回
False
。其他方法类似地工作。


如果你使用

functools.total_ordering
装饰器,你只需要实现例如
__lt__
__eq__
方法:

from functools import total_ordering

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

    def __eq__(self, other):
        ...

16
投票

这是 Python 3 中的一项重大且有意的更改。有关更多详细信息,请参见此处

  • 排序比较运算符(
    <
    <=
    >=
    >
    )在操作数没有有意义的自然排序时引发
    TypeError
    异常。因此,像
    1 < ''
    0 > None
    len <= len
    这样的表达不再有效,例如
    None < None
    提高
    TypeError
    而不是返回
    False
    。一个推论是,对异构列表进行排序不再有意义——所有元素必须相互比较。请注意,这不适用于
    ==
    !=
    运算符:不同不可比较类型的对象总是相互比较不相等。
  • builtin.sorted()
    list.sort()
    不再接受提供比较功能的
    cmp
    参数。请改用
    key
    参数。注意
    key
    reverse
    参数现在是“仅关键字”。
  • cmp()
    功能应该被视为消失了,不再支持
    __cmp__()
    特殊方法。使用
    __lt__()
    进行排序,使用
    __eq__()
    __hash__()
    ,以及根据需要进行其他丰富的比较。 (如果你真的需要
    cmp()
    功能,你可以使用表达式
    (a > b) - (a < b)
    作为
    cmp(a, b)
    的等价物。)

11
投票

Python3中丰富的六大比较运算符

__lt__(self, other) 
__le__(self, other) 
__eq__(self, other) 
__ne__(self, other) 
__gt__(self, other) 
__ge__(self, other) 

必须单独提供。这可以使用

functools.total_ordering
缩写。

然而,这在大多数情况下是相当难读和不切实际的。您仍然必须将类似的代码片段放入 2 个函数中 - 或者使用进一步的辅助函数。

所以大多数情况下我更喜欢使用混合类

PY3__cmp__
如下所示。这重新建立了单一的
__cmp__
方法框架,在大多数情况下,这在过去和现在都是非常清晰和实用的。仍然可以覆盖选定的丰富比较。

你的例子会变成:

 class point(PY3__cmp__):
      ... 
      # unchanged code

PY3__cmp__ 混合类:

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

0
投票

Python 2 领先于时代,因为用于实现

三向比较
__cmp__ 运算符目前在 C++(宇宙飞船运算符)和其他地方越来越受欢迎。请参阅Python 中的通用比较运算符?

原则上我喜欢 kxr 的 mixin 类,但根据我的口味,装饰器会更合适

PY3__cmp__
装饰器模仿@krx的回答和
functools.total_ordering
装饰器。

def cmp(a, b):
  return (a > b) - (a < b)

_convert = {
  '__eq__': lambda self, other: self.__cmp__(other) == 0,
  '__ne__': lambda self, other: self.__cmp__(other) != 0,
  '__lt__': lambda self, other: self.__cmp__(other) < 0,
  '__le__': lambda self, other: self.__cmp__(other) <= 0,
  '__gt__': lambda self, other: self.__cmp__(other) > 0,
  '__ge__': lambda self, other: self.__cmp__(other) >= 0,
}

def PY3__cmp__(cls):
  """Class decorator that fills in missing ordering methods when
     Python2-style `__cmp__(self, other)` method is provided."""
  if not hasattr(cls, '__cmp__'):
    raise ValueError('must define the __cmp__ Python2-style method')
  if sys.version_info < (3, 0, 0):
    return cls
  for op, opfunc in _convert.items():
    # Overwrite the `raise NotImplemented` comparisons inherited from object
    if getattr(cls, op, None) is getattr(object, op, None):
      setattr(cls, op, opfunc)
  return cls

(是的,我的代码库仍然保留一些关于缩进的 PEP-8 之前的想法。)

© www.soinside.com 2019 - 2024. All rights reserved.