以下代码
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__
以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):
...
这是Python 3中一个重大且经过深思熟虑的更改。
- 排序比较操作符(
<
,<=
,>=
,>
)在操作数没有有意义的自然排序时引发TypeError
异常。因此,像1 < ''
,0 > None
或len <= len
这样的表达式不再有效,例如None < None
引发TypeError
而不是返回False
。一个推论是,对异构列表进行排序不再有意义——所有元素必须彼此比较。注意,这不适用于==
和!=
操作符:不同的不可比较类型的对象总是彼此不相等比较。builtin.sorted()
和list.sort()
不再接受提供比较函数的cmp
参数。使用key
参数代替。注意:key
和reverse
参数现在是" 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之前关于缩进的想法。)