web-dev-qa-db-fra.com

Pourquoi ne puis-je pas utiliser la méthode __cmp__ dans Python 3 comme pour Python 2?

Le morceau de code suivant

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

fonctionne très bien dans Python 2, mais dans Python 3 je reçois une erreur:

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

Cela ne fonctionne que pour == et !=.

43
user1065734

Vous devez fournir les méthodes de comparaison riches pour la commande dans Python 3, qui sont __lt__ , __gt__ , __le__ , __ge__ , __eq__ et __ne__ . Voir aussi: PEP 207 - Comparaisons riches .

__cmp__ n'est plus plus utilisé.


Plus précisement, __lt__ prend self et other comme arguments, et doit indiquer si self est inférieur à other. Par exemple:

class Point(object):
    ...
    def __lt__(self, other):
        return ((self.x < other.x) and (self.y < other.y))

(Ce n'est pas une implémentation de comparaison raisonnable, mais il est difficile de dire ce que vous vouliez.)

Donc, si vous avez la situation suivante:

p1 = Point(1, 2)
p2 = Point(3, 4)

p1 < p2

Cela équivaudra à:

p1.__lt__(p2)

qui retournerait True.

__eq__ renverrait True si les points sont égaux et False sinon. Les autres méthodes fonctionnent de manière analogue.


Si vous utilisez functools.total_ordering décorateur, il vous suffit d'implémenter par exemple les __lt__ et __eq__ méthodes:

from functools import total_ordering

@total_ordering
class Point(object):
    def __lt__(self, other):
        ...

    def __eq__(self, other):
        ...
59
Jesse Dhillon

Ce fut un changement majeur et délibéré dans Python 3. Voir ici pour plus de détails.

  • Les opérateurs de comparaison de commande (<, <=, >=, >) Lèvent une exception TypeError lorsque les opérandes n'ont pas de ordre naturel significatif. Ainsi, des expressions comme 1 < '', 0 > None Ou len <= len Ne sont plus valides, et par exemple None < None Lève TypeError au lieu de renvoyer False. Un corollaire est que le tri d'une liste hétérogène n'a plus de sens - tous les éléments doivent être comparables entre eux. Notez que cela ne s'applique pas aux opérateurs == Et !=: Les objets de types incomparables différents se comparent toujours inégaux entre eux.
  • builtin.sorted() et list.sort() n'acceptent plus l'argument cmp fournissant une fonction de comparaison. Utilisez plutôt l'argument key. N.B. les arguments key et reverse sont désormais "mot clé uniquement".
  • La fonction cmp() doit être traitée comme disparue et la méthode spéciale __cmp__() n'est plus prise en charge. Utilisez __lt__() pour le tri, __eq__() avec __hash__(), et d'autres comparaisons riches si nécessaire. (Si vous avez vraiment besoin de la fonctionnalité cmp(), vous pouvez utiliser l'expression (a > b) - (a < b) Comme équivalent pour cmp(a, b).)
13
Ned Deily

En Python3, les six opérateurs de comparaison riches

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

doit être fourni individuellement. Cela peut être abrégé en utilisant functools.total_ordering.

Cela s'avère cependant assez illisible et peu pratique la plupart du temps. Vous devez toujours mettre des morceaux de code similaires dans 2 fonctions - ou utiliser une autre fonction d'assistance.

Donc, je préfère surtout utiliser la classe mixin PY3__cmp__ indiqué ci-dessous. Cela rétablit le single __cmp__ cadre de méthode, qui était et est assez clair et pratique dans la plupart des cas. On peut toujours remplacer les comparaisons riches sélectionnées.

Votre exemple deviendrait simplement:

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

La classe de mixage 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
8
kxr