Почему я не могу использовать метод __cmp__ в Python 3, как для Python 2? - PullRequest
36 голосов
/ 26 ноября 2011

Следующий фрагмент кода

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()

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

Ответы [ 3 ]

49 голосов
/ 26 ноября 2011

Вам необходимо предоставить расширенные методы сравнения для заказа в 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):
        ...
11 голосов
/ 26 ноября 2011

Это было серьезное и преднамеренное изменение в 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).)
7 голосов
/ 26 августа 2016

В Python3 шесть операторов расширенного сравнения

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

должны предоставляться индивидуально.Это может быть сокращено с помощью functools.total_ordering.

Однако в большинстве случаев это оказывается довольно нечитабельным и непрактичным.Тем не менее, вы должны поместить подобные куски кода в 2 функции - или использовать еще одну вспомогательную функцию.

Так что в основном я предпочитаю использовать класс mixin PY3__cmp__, показанный ниже.Это восстанавливает единую структуру __cmp__ метода, которая была и остается достаточно ясной и практичной в большинстве случаев.Можно по-прежнему переопределять выбранные расширенные сравнения.

Ваш пример будет просто:

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

Класс mixin 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
...