1.6. La surcharge d’opérateurs

Nous avons maintenant montré que les classes permettaient de créer des types bien plus sophistiqués et puissants que les types élémentaires. Cependant, comme nous allons le voir, nos objets interagissent pour l’instant plutôt mal avec l’écosystème de Python.

Définissons une classe qui représente un nombre complexe, avec deux attributs de type float pour stocker la partie réelle et la partie imaginaire, ainsi que quelques méthodes utiles.

import math


class Complex:

    def __init__(self, real, img):
        self.real = real
        self.img = img

    def magnitude(self):
        return math.sqrt(self.real ** 2 + self.img ** 2)

    def conjugate(self):
        return Complex(self.real, -self.img)


a = Complex(4, 2)

print(a.real)
print(a.img)
print(a.magnitude())
print(a.conjugate().img)
4
2
4.47213595499958
-2

Les attributs et méthodes de l’objet se comportent bien comme prévu. Essayons maintenant d’afficher la valeur de l’objet et d’additionner deux nombres complexes entre eux.

print(a)
c = Complex(-3, 8)
print(a + c)
<__main__.Complex object at 0x7f568703bf10>
Traceback (most recent call last):
  File "<input>", line...
    print(a + c)
TypeError: unsupported operand type(s) for +: 'Complex' and 'Complex'

La console affiche <__main__.Complexe object at ...> (résultat variable) au lieu d’une représentation mathématique classique comme 4 + 2i. Pire encore, impossible d’additionner entre eux deux de nos nombres complexes à l’aide de l’opérateur +, comme l’indique le message d’erreur.

Pour cause, Python ne sait pas afficher nos objets ni les additionner, car on ne lui a pas expliqué comment faire. Comme pour certains objets (mais pas tous !) les opérateurs de Python se montrent très pratiques, il existe un moyen de spécifier comment additionner, comparer, afficher, etc. nos objets personnalisés. On appelle cela la surcharge d’opérateurs.

1.6.1. La représentation textuelle

Pour qu’un objet puisse être converti en chaîne de caractères, et ainsi lui offrir une représentation textuelle, il faut mettre en œuvre la méthode spéciale __str__(). Elle est appelée lorsque l’objet doit être converti en chaîne de caractère et elle renvoie le résultat. Sa mise en œuvre par défaut renvoie l’adresse mémoire à laquelle l’objet est stocké (d’où l’affichage cryptique que nous avons rencontré précédemment).

class Complex:

    def __init__(self, real, img):
        self.real = real
        self.img = img

    # fonction appelée chaque fois que l'on doit convertir l'objet en chaîne de caractères
    def __str__(self):
        # on renvoie une représentation textuelle
        return f"{self.real} + {self.img}i"


a = Complex(4, 2)
b = Complex(-3, 8)

print(a)
print(b)
4 + 2i
-3 + 8i

1.6.2. La surcharge des opérateurs arithmétiques

La surcharge des opérateurs mathématiques se fait de manière similaire à celle de la représentation textuelle, en mettant en œuvre les méthodes spéciales dédiées. La seule différence notable est que ces méthodes spéciales possèdent un argument prenant la valeur du second opérande.

Méthodes de surcharge des opérateurs arithmétiques

Opérateur arithmétique

Méthode spéciale

x + y

x.__add__(y)

x - y

x.__sub__(y)

x * y

x.__mul__(y)

x / y

x.__truediv__(y)

x // y

x.__floordiv__(y)

x % y

x.__mod__(y)

x ** y

x.__pow__(y)

Voyons un exemple avec l’opérateur d’addition.

class Complex:

    def __init__(self, real, img):
        self.real = real
        self.img = img

    def __str__(self):
        return f"{self.real} + {self.img}i"

    def __add__(self, other):
        return Complex(self.real + other.real, self.img + other.img)


a = Complex(4, 2)
b = Complex(-3, 8)

print(a + b)
1 + 10i

Dans cet exemple, l’instruction print(a + b) appelle la méthode d’addition __add__() pour additionner les deux nombres complexes puis la méthode de conversion __str__() pour afficher le résultat.

1.6.3. La surcharge des opérateurs de comparaison

La surcharge des opérateurs de comparaison se fait de la même manière que pour les opérateurs arithmétiques.

Méthodes de surcharge des opérateurs de comparaison

Opérateur de comparaison

Méthode spéciale

x < y

x.__lt__(y)

x > y

x.__gt__(y)

x <= y

x.__le__(y)

x >= y

x.__ge__(y)

x == y

x.__eq__(y)

x != y

x.__ne__(y)

Voyons un exemple avec l’opérateur d’égalité.

class Complex:

    def __init__(self, real, img):
        self.real = real
        self.img = img

    def __eq__(self, other):
        return self.real == other.real and self.img == other.img


a = Complex(4, 2)
b = Complex(-3, 8)

print(a == b)
print(b == Complex(-3, 8))
False
True

1.6.4. Opérateurs supplémentaires

Les autres opérateurs du langage Python peuvent également être surchargés. Nous résumons ci-dessous les méthodes spéciales correspondantes.

Méthodes de surcharge des opérateurs d’assignation

Opérateur d’assignation

Méthode spéciale

x += y

x.__iadd__(y)

x -= y

x.__isub__(y)

x *= y

x.__imul__(y)

x /= y

x.__idiv__(y)

x //= y

x.__ifloordiv__(y)

x %= y

x.__imod__(y)

x **= y

x.__ipow__(y)

Méthodes de surcharge des opérateurs unaires

Opérateur unaire

Méthode spéciale

-x

x.__neg__()

+x

x.__pos__()

~x

x.__invert__()