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



以下代码

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 3中提供丰富的排序比较方法,它们是__lt__, __gt__, __le__, __ge__, __eq____ne__。参见:PEP 207—丰富的比较。

__cmp__ is no不再使用


具体来说,__lt__selfother为参数,需要返回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):
        ...

这是Python 3中一个重大且经过深思熟虑的更改。

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

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

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

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

然而,这在大多数情况下是相当不可读和不实用的。你仍然需要把类似的代码片段放在两个函数中——或者使用另一个辅助函数。

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

你的例子将变成:

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

PY3__cmp__ mixin类:

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

Python 2是领先的,因为__cmp__运算符实现三向比较目前在c++(太空船运算符)和其他地方越来越流行。

原则上我喜欢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之前关于缩进的想法。)

相关内容

  • 没有找到相关文章

最新更新