6.1 Méthodes magiques
La surcharge d'opérateurs en Python te permet de définir ou modifier le comportement des opérateurs intégrés (comme +
, -
, *
, /
) pour les classes personnalisées. Cela se fait à l'aide de méthodes spéciales, également appelées méthodes magiques.
Par exemple, dans ta classe, tu peux surcharger les opérateurs de comparaison :
Opérateur | Méthode sans underscore | Signature de la méthode |
---|---|---|
== | eq() |
__eq__(self, other) |
!= | ne() |
__ne__(self, other) |
< | lt() |
__lt__(self, other) |
<= | le() |
__le__(self, other) |
> | gt() |
__gt__(self, other) |
>= | ge() |
__ge__(self, other) |
Supposons que tu as écrit ta classe et que tu souhaites que les objets de ta classe soient comparés comme tu le souhaites. Tu dois simplement implémenter la méthode «__eq__»
dans ta classe, et Python l'appellera chaque fois que des objets de ta classe seront comparés dans le code.
Exemple :
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
# Utilisation
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1 == v2) # Affichera: True
print(v1 == v3) # Affichera: False
Chaque fois que tu compares deux de tes objets, Python vérifie s'il n'y a pas une fonction «__eq__»
implémentée. Si elle existe, il l'appelle. Sinon, il comparera simplement les références des objets.
En fait, dans l'exemple ci-dessus, c'est comme si on écrivait (il faut juste ajouter la vérification de l'existence de la méthode) :
# Utilisation
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(4, 5)
print(v1.__eq__(v2)) # Affichera: True
print(v1.__eq__(v3)) # Affichera: False
6.2 Liste de tous les opérateurs
Il existe 6 groupes d'opérateurs disponibles pour la surcharge.
Opérateurs arithmétiques :
Opérateur | Méthode sans underscore | Signature de la méthode |
---|---|---|
+ | add |
__add__(self, other) |
- | sub |
__sub__(self, other) |
* | mul |
__mul__(self, other) |
/ | truediv |
__truediv__(self, other) |
// | floordiv |
__floordiv__(self, other) |
% | mod |
__mod__(self, other) |
** | pow |
__pow__(self, other) |
Opérateurs de comparaison :
Opérateur | Méthode sans underscore | Signature de la méthode |
---|---|---|
== | eq |
__eq__(self, other) |
!= | ne |
__ne__(self, other) |
< | lt |
__lt__(self, other) |
<= | le |
__le__(self, other) |
> | gt |
__gt__(self, other) |
>= | ge |
__ge__(self, other) |
Opérateurs logiques :
Opérateur | Méthode sans underscore | Signature de la méthode |
---|---|---|
& | and |
__and__(self, other) |
| | or |
__or__(self, other) |
^ | xor |
__xor__(self, other) |
~ | invert |
__invert__(self) |
Opérateurs d'indexation et de découpage :
Opérateur | Méthode |
---|---|
obj[key] | __getitem__(self, key) |
obj[key] = value | __setitem__(self, key, value) |
del obj[key] | __delitem__(self, key) |
Opérateurs unaires :
Opérateur | Méthode |
---|---|
- | __neg__(self) |
+ | __pos__(self) |
abs() | __abs__(self) |
~ | __invert__(self) |
Opérateurs d'affectation :
Opérateur | Méthode |
---|---|
+= | __iadd__(self, other) |
-= | __isub__(self, other) |
*= | __imul__(self, other) |
/= | __itruediv__(self, other) |
//= | __ifloordiv__(self, other) |
%= | __imod__(self, other) |
**= | __ipow__(self, other) |
Peut-être que c'est pour ça que Python est si lent - à chaque fois avant d'exécuter un opérateur, il cherche une fonction équivalente dans la classe et tous ses parents. Mais cela permet d'écrire le code le plus compact au monde :)
6.3 Opérateur d'indexation
Le fait que l'on puisse comparer des objets ou soustraire des ensembles semble quelque peu évident. Et tu le devineras toi-même si tu écris une classe qui implique sur elle des opérations logiques ou mathématiques.
Je veux te montrer un exemple intéressant - comme l'opérateur d'indexation. Commençons directement avec le code de l'exemple :
class CustomList:
def __init__(self, data):
self.data = data
def __getitem__(self, index):
return self.data[index]
def __setitem__(self, index, value):
self.data[index] = value
def __delitem__(self, index):
del self.data[index]
def __repr__(self):
return repr(self.data)
# Utilisation
c_list = CustomList([1, 2, 3, 4, 5])
print(c_list[1]) # Affichera: 2
c_list[1] = 10
print(c_list) # Affichera: [1, 10, 3, 4, 5]
del c_list[1]
print(c_list) # Affichera: [1, 3, 4, 5]
Ici, nous voyons un exemple de trois opérations :
Lecture de données par index
Écriture de données par index
Et même suppression de données par index.
Et il n'est pas du tout nécessaire que les données soient stockées sous forme de liste à l'intérieur. Ou l'index ne doit pas nécessairement être un nombre. Par exemple, la classe dictionary
(dictionnaire) est implémentée exactement ainsi.
Créons SuperList
Tu te souviens de la classe list
? On peut y attribuer des éléments, mais seulement avec les indices qui existent déjà. Faisons notre propre classe, appelons-la SuperList
, à laquelle on peut accéder pour chaque élément à n'importe quel index :
Si l'index < 0, nous insérons l'élément au début
Si l'index >= len, nous ajoutons l'élément à la fin
Dans les autres cas, nous retournons simplement l'élément
Exemple :
class SuperList(list):
def __init__(self, value):
super().__init__(value)
def __setitem__(self, index, value):
if index >= len(self):
super().append(value)
elif index < 0:
super().insert(0, value)
else:
super().__setitem__(index, value)
lst = SuperList([1, 2, 3])
lst[200] = 100
lst[-200] = 99
print(lst) # [99, 1, 2, 3, 100]
Donc, la surcharge des index est une opportunité géniale, et je te recommande de l'utiliser en pratique. C'est tout pour aujourd'hui.
GO TO FULL VERSION