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

wbrvyc0a  于 2023-04-10  发布在  Python
关注(0)|答案(4)|浏览(169)

下面这段代码

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()

它只适用于==!=

6za6bjd0

6za6bjd01#

你需要提供丰富的比较方法来在Python 3中排序,它们是__lt____gt____le____ge____eq____ne__。另请参阅:PEP 207 -- Rich Comparisons
__cmp__不再使用。
具体来说,__lt__selfother为参数,需要返回self是否小于other。例如:

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

(This不是一个明智的比较实现,但很难判断您想要的是什么。)
所以如果你有以下情况:

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):
        ...
oknrviil

oknrviil2#

这是Python 3中的一个重大而深思熟虑的变化。更多细节请参见此处。

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

bxfogqkk3#

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

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

必须单独提供。可以缩写为functools.total_ordering
然而,这在大多数情况下是不可读和不切实际的。你仍然必须把类似的代码片段放在2个函数中-或者使用另一个帮助函数。
因此,我更喜欢使用下面的mixin类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
wfveoks0

wfveoks04#

Python 2是超前的,因为__cmp__运算符实现three-way comparison目前在C++(宇宙飞船运算符)和其他地方越来越流行。参见Universal Comparison Operator In 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

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

相关问题