Подтвердить что ты не робот

Почему я не могу использовать метод __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()

Он работает только для == и !=.

4b9b3361

Ответ 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