Почему я не могу использовать метод __cmp__ в Python 3 как для Python 2?
Следующий фрагмент кода
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()
Он работает только для ==
и !=
.
Ответы
Ответ 1
Вам необходимо предоставить богатые методы сравнения для заказа в Python 3, которые являются __lt__
, __gt__
, __le__
, __ge__
, __eq__
и __ne__
. См. Также: PEP 207 - Богатые сравнения.
__cmp__
больше не используется.
Более конкретно, __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):
...
Ответ 2
Это было серьезное и преднамеренное изменение в Python 3. См. Здесь для более подробной информации.
- Операторы сравнения порядка (
<
, <=
, >=
, >
) создают исключение TypeError
когда операнды не имеют значимого естественного упорядочения. Таким образом, выражения типа 1 < ''
, 0 > None
или len <= len
более недействительны и, например, None < None
вызывает TypeError
вместо возврата False
. Следствием является то, что сортировка гетерогенного списка больше не имеет смысла - все элементы должны быть сопоставимы друг с другом. Обратите внимание, что это не относится к операторам ==
и !=
: Объекты разных несравнимых типов всегда сравниваются неравномерно друг с другом. -
builtin.sorted()
и list.sort()
больше не принимают аргумент cmp
предоставляющий функцию сравнения. Вместо этого используйте key
аргумент. NB, key
и reverse
аргументы теперь являются ключевыми словами. - Функция
cmp()
следует рассматривать как ушедшую, а специальный метод __cmp__()
больше не поддерживается. Используйте __lt__()
для сортировки __eq__()
с __hash__()
и другими богатыми сравнениями по мере необходимости. (Если вам действительно нужна функция cmp()
, вы можете использовать выражение (a > b) - (a < b)
как эквивалент для cmp(a, b)
.)
Ответ 3
В Python3 шесть богатых операторов сравнения
__lt__(self, other)
__le__(self, other)
__eq__(self, other)
__ne__(self, other)
__gt__(self, other)
__ge__(self, other)
должен предоставляться индивидуально. Это можно сократить с помощью functools.total_ordering
.
Это, однако, получается довольно непроницаемым и непрактичным в большинстве случаев. Тем не менее вам нужно поместить похожие фрагменты кода в 2 funcs - или использовать дополнительную вспомогательную функцию.
Поэтому в основном я предпочитаю использовать класс 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
Ответ 4
Определите правильные методы.