下面一段代码
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()
它只适用于
==
和!=
.
__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):
...
这是 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)
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 = 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++(宇宙飞船运算符)和其他地方越来越受欢迎。请参阅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 之前的想法。)