Surcharge des opérateurs en Python

Denis Hulo

Objectif : redéfinir dans une classe Python les opérateurs d'addition, de multiplication et de puissance pour les nombres complexes.

Niveau requis : débutant.

Commentez cet article : Commentez

- Titre : Surcharge des opérateurs en Python

- Auteur : Denis Hulo

- Parution : 12 avril 2022

- Public visé :

- Licence : Copyright ® 2022 Denis Hulo. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Nombres complexes

- II-A. Définition

- II-B. Représentation

- II-B-1. Forme algébrique

- II-B-2. Forme trigonométrique

- II-B-3. Opérations sur les nombres complexes

- II-B-3-a. Addition

- II-B-3-b. Multiplication

- III. Création de la classe Complexe

- III-A. Surcharge de l'opérateur d'addition

- III-B. Surcharge de l'opérateur de multiplication

- III-C. Surcharge de l'opérateur de puissance

- III-D. Surcharge de l'opérateur de comparaison « == »

- IV. Conclusion

- V. Module Python

- VI. Remerciements

I - Introduction

La surcharge d’opérateur permet de redéfinir un opérateur dans une classe.

Par exemple, en Python l’opérateur « + » est surchargé par la classe int et la classe str :

- on peut ainsi réaliser une addition classique entre deux entiers : print(1+2) affiche 3 ;

- ou concaténer deux chaînes de caractères : print("bon"+"jour") renvoie "bonjour".

Notre objectif est de redéfinir dans une classe Python les opérateurs d'addition, de multiplication et de puissance pour les nombres complexes.

II - Nombres complexes

II-A - Définition

En mathématiques, l'ensemble des nombres complexes est créé comme extension de l'ensemble des nombres réels, contenant en particulier un nombre imaginaire noté i, tel que i^2 = −1. Le carré de (−i) est aussi égal à −1 : (−i)2 = −1.

Tout nombre complexe peut s'écrire sous la forme a + ib où a et b sont des nombres réels.

Représentation graphique du nombre complexe

II-B - Représentation

II-B-1 - Forme algébrique

Un nombre complexe z se présente en général sous forme algébrique comme une somme a + ib,
où a et b sont des nombres réels quelconques
et où i (l’unité imaginaire) est un nombre particulier tel que i^2 = –1.

II-B-2 - Forme trigonométrique

Pour tout couple de réels (a, b) différent du couple (0,0), il existe un réel positif r et une famille d'angles θ déterminés à un multiple de 2π près tels que a = r cos(θ) et b = r sin(θ).

Tout nombre complexe non nul peut donc s'écrire sous une forme trigonométrique :
z = r (cos(θ) + i sin(θ)) avec r > 0.

Le réel positif r est appelé le module du complexe z et il est noté |z|. Le réel θ est appelé un argument du complexe z et il est noté arg(z).

II-B-3 - Opérations sur les nombres complexes

II-B-3-a - Addition

L'addition de 2 nombres complexes z_1 = a + ib et z_2 = c + id écrits sous forme algébrique, est définie par :

(a + ib) + (c + id) = (a+b) + i(b+d)

II-B-3-b - Multiplication

La multiplication de 2 nombres complexes z_1 = a + ib et z_2 = c + id écrits sous forme algébrique, est définie par :

(a + ib) × (c + id) = (ac - bd) + i(ad + bc)

Pour avoir plus d'informations sur le sujet, je vous invite à consulter la page de Wikipedia Nombre complexe.

III - Création de la classe Complexe

Pour définir ces nombres complexes en Python et pouvoir réaliser des opérations entre eux, il nous faut créer une classe Complexe.

Notre classe comportera un constructeur, c'est-à-dire une méthode particulière __init__() dont le code est exécuté quand la classe est instanciée.

Elle va nous permettre de définir les parties réelle et imaginaire du nombre complexe au moment de la création de l'objet :

class Complexe:

def __init__(self, part_reel=0, part_imag=0): # méthode constructeur de la classe
self.reel = part_reel # on définit la partie réelle du nombre complexe
self.imag = part_imag # on définit la partie imaginaire du nombre complexe

def __str__(self): # permet d'afficher le nombre complexe sous la forme algébrique a + bi
return "{0} + {1}i".format(self.reel, self.imag) if self.imag>=0 else "{0} - {1}i".format(self.reel, abs(self.imag))

La méthode __str__() permet d'afficher un nombre complexe sous la forme a + bi.

Pour tester ces méthodes, nous ajoutons simplement deux lignes au module :

z = Complexe(1, 2) # création de l'objet Complexe : 1 + 2i
print(z) # affiche le nombre complexe

Le code affiche :

1 + 2i

III-A - Surcharge de l'opérateur d'addition

Pour surcharger l'opérateur « + » et pouvoir ainsi réaliser l'addition de 2 nombres complexes, nous devons ajouter une méthode __add __ () à la classe :

class Complexe:

def __init__(self, part_reel=0, part_imag=0): # méthode constructeur de la classe
self.reel = part_reel # on définit la partie réelle du nombre complexe
self.imag = part_imag # on définit la partie imaginaire du nombre complexe

def __str__(self): # permet d'afficher le nombre complexe sous la forme algébrique a + bi
return "{0} + {1}i".format(self.reel, self.imag) if self.imag>=0 else "{0} - {1}i".format(self.reel, abs(self.imag))

def __add__(self, other): # méthode permettant de redéfinir l'opérateur « + » pour 2 nombres complexes : z1 + z2 = (a + bi) + (c + di) = (a+c) + (b+d)i
part_reel = self.reel + other.reel # on évalue la partie réelle du nombre complexe résultat de l'addition
part_imag = self.imag + other.imag # on évalue la partie imaginaire du nombre complexe résultat de l'addition
return Complexe(part_reel, part_imag) # renvoie le nombre complexe résultat de l'addition

Cette méthode permet donc de redéfinir l'opération « + » pour les nombres complexes en utilisant l'égalité :

(a + bi) + (c + di) = (a+c) + (b+d)i

Pour tester l'opérateur d'addition portant sur 2 objets de la classe Complexe, nous ajoutons simplement ces lignes de code :

z1 = Complexe(1, 2) # création du 1er objet de la classe Complexe : 1 + 2i
z2 = Complexe(2, 3) # création du 2e objet Complexe : 2 + 3i

print(z1+z2) # affiche le résultat de l'addition

Le code affiche :

3 + 5i

III-B - Surcharge de l'opérateur de multiplication

Pour surcharger l'opérateur « * » et l'appliquer à 2 nombres complexes, nous devons également ajouter une méthode __mul __ () à la classe :

class Complexe:

def __init__(self, part_reel=0, part_imag=0): # méthode constructeur de la classe
self.reel = part_reel # on définit la partie réelle du nombre complexe
self.imag = part_imag # on définit la partie imaginaire du nombre complexe

def __str__(self): # permet d'afficher le nombre complexe sous la forme algébrique a + bi
return "{0} + {1}i".format(self.reel, self.imag) if self.imag>=0 else "{0} - {1}i".format(self.reel, abs(self.imag))

def __add__(self, other): # méthode permettant de redéfinir l'opérateur « + » pour 2 nombres complexes : z1 + z2 = (a + bi) + (c + di) = (a+c) + (b+d)i
part_reel = self.reel + other.reel # on évalue la partie réelle du nombre complexe résultat de l'addition
part_imag = self.imag + other.imag # on évalue la partie imaginaire du nombre complexe résultat de l'addition
return Complexe(part_reel, part_imag) # renvoie le nombre complexe résultat de l'addition

def __mul__(self, other): # méthode permettant de redéfinir l'opérateur « * » pour 2 nombres complexes : z1 * z2 = (ac - bd) + (ad + bc)*i
part_reel = self.reel * other.reel - self.imag * other.imag # part_reel = (ac - bd)
part_imag = self.reel * other.imag + self.imag * other.reel # part_imag = (ad + bc)
return Complexe(part_reel, part_imag) # renvoie le nombre complexe résultat de la multiplication

Cette méthode offre donc la possibilité de redéfinir l'opération de multiplication pour 2 nombres complexes en utilisant l'égalité :

(a + bi) x (c + di) = (ac - bd) + (ad + bc)i

Pour tester l'opérateur de multiplication portant sur 2 objets de la classe Complexe, nous ajoutons simplement ces lignes :

z1 = Complexe(1, 2) # création du 1er objet de la classe Complexe : 1 + 2i
z2 = Complexe(2, 3) # création du 2e objet de la classe Complexe : 2 + 3i

print(z1*z2) # affiche le résultat du produit

Le code affiche :

-4 + 7i

III-C - Surcharge de l'opérateur de puissance

Maintenant que nous avons redéfini les opérateurs d'addition et de multiplication dans notre classe Complexe, nous pouvons ajouter une méthode __pow__() qui va permettre d'évaluer un nombre complexe z élevé à la puissance n.

class Complexe:

def __init__(self, part_reel=0, part_imag=0): # méthode constructeur de la classe
self.reel = part_reel # on définit la partie réelle du nombre complexe
self.imag = part_imag # on définit la partie imaginaire du nombre complexe

def __str__(self): # permet d'afficher le nombre complexe sous la forme algébrique a + bi
return "{0} + {1}i".format(self.reel, self.imag) if self.imag>=0 else "{0} - {1}i".format(self.reel, abs(self.imag))

def __add__(self, other): # méthode permettant de redéfinir l'opérateur « + » pour 2 nombres complexes : z1 + z2 = (a + bi) + (c + di) = (a+c) + (b+d)i
part_reel = self.reel + other.reel # on évalue la partie réelle du nombre complexe résultat de l'addition
part_imag = self.imag + other.imag # on évalue la partie imaginaire du nombre complexe résultat de l'addition
return Complexe(part_reel, part_imag) # renvoie le nombre complexe résultat de l'addition

def __mul__(self, other): # méthode permettant de redéfinir l'opérateur « * » pour 2 nombres complexes : z1 * z2 = (ac - bd) + (ad + bc)*i
part_reel = self.reel * other.reel - self.imag * other.imag # part_reel = (ac - bd)
part_imag = self.reel * other.imag + self.imag * other.reel # part_imag = (ad + bc)
return Complexe(part_reel, part_imag) # renvoie le nombre complexe résultat de la multiplication

def __pow__(self, n): # méthode permettant de redéfinir l'opérateur de puissance : self ** n
z = Complexe(1,0) # on initialise la variable objet z avec la valeur 1 élément neutre pour la multiplication de nombres complexes
for i in range(n): # nous multiplions n fois z par self à l'aide de l'opérateur *
z = z*self # équivalent à : z = z.__mul__(self)
return z # renvoie le nombre complexe résultat de l'opération (self ** n)

Nous testons maintenant l'opérateur pour (1 + i) ** 3 :

z = Complexe(1, 1)
print(z**3)

Le code renvoie :

-2 + 2i

Vérification

z^3 = (1 + i)3

z^3 = (1 + i) × (1 + i) × (1 + i)

En développant et en réduisant le produit des 2 premiers facteurs, on obtient :

z^3 = (0 + 2i) × (1 + i)

Enfin, en développant et en réduisant une seconde fois, on a bien :

z^3 =-2 + 2i

Tableau de quelques opérateurs et de leur méthode correspondante en Python :

Opérateur

Expression

Interprétation Python

Addition

z1 + z2

z1.__add__(z2)

Soustraction

z1 - z2

z1.__sub__(z2)

Multiplication

z1 * z2

z1.__mul__(z2)

Puissance

z1 ** z2

z1.__pow__(z2)

Division

z1 / z2

z1.__truediv__(z2)

Division entière

z1 // z2

z1.__floordiv__(z2)

Modulo

z1 % z2

z1.__mod__(z2)

...

...

...

III-D - Surcharge de l'opérateur de comparaison « == »

Pour surcharger l'opérateur « == » et pouvoir ainsi tester si 2 nombres complexes sont égaux, nous ajoutons finalement une méthode __eq__ () à notre classe :

class Complexe:

def __init__(self, part_reel=0, part_imag=0): # méthode constructeur de la classe
self.reel = part_reel # on définit la partie réelle du nombre complexe
self.imag = part_imag # on définit la partie imaginaire du nombre complexe

#------------------------------------------------------

def __eq__(self, other): # méthode permettant de redéfinir l'opérateur « == » pour 2 nombres complexes
return (self.reel==other.reel) and (self.imag==other.imag) # renvoie True si les parties réelles et imaginaires des 2 nombres complexes sont égales

Cette méthode permet donc de redéfinir l'opérateur de comparaison « == » pour deux nombres complexes en testant si leurs parties réelles et imaginaires sont égales :

z1 = Complexe(1, 2) # création du 1er objet de la classe Complexe : 1 + 2i
z2 = Complexe(1, 2) # création de 2e objet Complexe : 1 + 2i

print(z1==z2) # affiche le résultat de la comparaison

Le code affiche :

True

Tableau de quelques opérateurs de comparaison et de leur méthode correspondante en Python :

Opérateur

Expression

Interprétation Python

Inférieur à

z1 < z2

z1.__lt__(z2)

Inférieur ou égal à

z1 <= z2

z1.__le__(z2)

Egal

z1 == z2

z1.__eq__(z2)

...

...

...

Si vous souhaitez avoir une liste plus complète des opérateurs, je vous invite à consulter cette page.

À noter qu'il existe également un module cmath pour Python contenant des fonctions mathématiques pour les nombres complexes.

IV - Conclusion

Après avoir défini les opérations d'addition et de multiplication pour les nombres complexes, nous avons pu redéfinir les opérateurs « + », « * » et « ** » dans une classe Python à l'aide de la surcharge d'opérateurs.

Chacun pourra ensuite librement ajouter d'autres opérateurs à la classe Complexe ou bien en créer une autre, basée par exemple sur la représentation trigonométrique des nombres complexes.

V - Module Python

Il contient le code complet de la classe Complexe :

Classe Complexe

class Complexe:

def __init__(self, part_reel=0, part_imag=0): # méthode constructeur de la classe
self.reel = part_reel # on définit la partie réelle du nombre complexe
self.imag = part_imag # on définit la partie imaginaire du nombre complexe

def __str__(self): # permet d'afficher le nombre complexe sous la forme algébrique a + bi
return "{0} + {1}i".format(self.reel, self.imag) if self.imag>=0 else "{0} - {1}i".format(self.reel, abs(self.imag))

def __add__(self, other): # méthode permettant de redéfinir l'opérateur « + » pour 2 nombres complexes : z1 + z2 = (a + bi) + (c + di) = (a+c) + (b+d)i
part_reel = self.reel + other.reel # on évalue la partie réelle du nombre complexe résultat de l'addition
part_imag = self.imag + other.imag # on évalue la partie imaginaire du nombre complexe résultat de l'addition
return Complexe(part_reel, part_imag) # renvoie le nombre complexe résultat de l'addition

def __mul__(self, other): # méthode permettant de redéfinir l'opérateur « * » pour 2 nombres complexes : z1 * z2 = (ac - bd) + (ad + bc)*i
part_reel = self.reel * other.reel - self.imag * other.imag # part_reel = (ac - bd)
part_imag = self.reel * other.imag + self.imag * other.reel # part_imag = (ad + bc)
return Complexe(part_reel, part_imag) # renvoie le nombre complexe résultat de la multiplication

def __pow__(self, n): # méthode permettant de redéfinir l'opérateur de puissance : self ** n
z = Complexe(1,0) # on initialise la variable objet z avec la valeur 1 élément neutre pour la multiplication de nombres complexes
for i in range(n): # nous multiplions n fois z par self à l'aide de l'opérateur *
z = z*self # équivalent à : z = z.__mul__(self)
return z # renvoie le nombre complexe résultat de l'opération (self ** n)
##
def __eq__(self, other): # méthode permettant de redéfinir l'opérateur « == » pour 2 nombres complexes
return (self.reel==other.reel) and (self.imag==other.imag) # renvoie True si les parties réelles et imaginaires des 2 nombres complexes sont égales

z = Complexe(1, 1) # on créé l'objet de la classe Complexe : 1 + i
print(z**3) # on affiche le résultat de l'opération z**3

VI - Remerciements

Je tiens à remercier Malick pour le suivi de cet article, ainsi que escartefigue pour sa relecture.