Les méthodes magiques, souvent appelées dunder methods, sont des méthodes dont les noms sont réservés par Python. Ces méthodes représentent des protocoles internes au langage.
En les implémentant dans vos classes, vous permettez à vos objets de s'interfacer avec des fonctionnalités de base comme les opérateurs, les boucles et les fonctions intégrées.
Les méthodes spéciales sont facilement identifiables, car elles sont entourées de doubles underscores. Exemple : __init__.
Qu'est-ce qu'une méthode magique ?
Généralement, les méthodes magiques ne sont pas directement appelées par leur nom. C'est l'interpréteur Python qui s'en charge lors d'opérations spécifiques. Par exemple, __str__ sera appelée si l'on utilise la fonction str() sur une instance ou avec print(), __add__ sera exécutée si l'opérateur + est utilisé et __len__ sera invoquée lors de l'appel de la fonction len().
Vous pouvez définir un comportement similaire à celui des types natifs (list, dict, str, etc.) qui embarquent leurs propres méthodes magiques.
class Score: def __init__(self, points): self.points = points def __add__(self, autre): # Cette méthode est appelée par l'opérateur '+' return Score(self.points + autre.points) def __str__(self): # Cette méthode est appelée par la fonction 'print()' return f"Le score est de : {self.points} points" score_a = Score(10) score_b = Score(20) # Python voit '+', il appelle en secret score_a.__add__(score_b) total = score_a + score_b # Python voit 'print', il appelle en secret total.__str__() print(total) # Affiche : Le score est de : 30 points
Inscrivez-vous gratuitement pour modifier et exécuter du code Python directement dans votre navigateur.
L'automatisation avec les Dataclasses
Depuis Python 3.7, le module dataclasses permet d'automatiser la génération par défaut des méthodes magiques __init__, __repr__ et __eq__. Je vous invite d'ailleurs à lire notre glossaire sur le sujet.
Cette automatisation souligne l'importance de ces méthodes magiques dans la conception orientée objet en Python.
from dataclasses import dataclass @dataclass class Produit: nom: str prix: float # Sans rien écrire d'autre, Python a déjà créé : # 1. __init__ pour accepter 'nom' et 'prix' # 2. __repr__ pour un affichage propre # 3. __eq__ pour comparer deux produits iphone1 = Produit("iPhone", 999.0) iphone2 = Produit("iPhone", 999.0) # Ici, print() cherche __str__ mais se rabat sur le __repr__ # généré par la dataclass car __str__ n'est pas définie par défaut. print(iphone1) # Affiche : Produit(nom='iPhone', prix=999.0) # Comparaison entre deux instances (utilise le __eq__ généré automatiquement) print(iphone1 == iphone2) # Affiche : True car les données sont identiques
Quelques méthodes magiques incontournables
Initialisation et représentation
Sans doute la plus connue des méthodes magiques, __init__(self, ...) est utilisée pour gérer le cycle de vie d'un objet.
Pour l'affichage, vous pouvez définir deux méthodes :
-
__str__(self)définit ce que retournent les fonctionsprintetstr. Elle doit être lisible par un humain -
__repr__(self)représente l'objet de manière officielle pour le débogage. Elle est appelée avec la fonctionrepr. Si__str__est manquante,printetstrutiliseront cette méthode
class Robot: def __init__(self, nom): self.nom = nom def __str__(self): return f"Je suis le robot {self.nom}." def __repr__(self): return f"Robot(nom='{self.nom}')" r2d2 = Robot("R2D2") print(r2d2) # Appelle automatiquement __str__ print(repr(r2d2)) # Appelle automatiquement __repr__
Inscrivez-vous gratuitement pour modifier et exécuter du code Python directement dans votre navigateur.
Accès aux données
Si vous avez besoin d'utiliser l'indexation ou si vous souhaitez retourner la longueur de l'objet :
-
__len__(self)permet d'implémenter le calcul de la longueur de vos objets. Elle sera appelée parlen -
__getitem__(self, index)permet d'utiliser la syntaxe des crochetsmonobjet[index]pour l'indexation et le slicing. Elle reçoit donc l'index (ou une slice) en paramètre -
__iter__(self)retourne un itérateur pour l'objet et sera utilisée par les bouclesfor
Si __iter__ n'est pas définie, Python utilisera __getitem__ en commençant par l'index 0 et s'arrêtera quand IndexError sera levée.
class CalendrierAvent: def __init__(self, surprises): self.surprises = surprises def __len__(self): return len(self.surprises) def __getitem__(self, jour): return self.surprises[jour] calendrier = CalendrierAvent(["chocolat", "bonbon", "jouet"]) print(len(calendrier)) # 3 print(calendrier[0]) # chocolat print(calendrier[1]) # bonbon for surprise in calendrier: print(f"Aujourd'hui : {surprise}")
Inscrivez-vous gratuitement pour modifier et exécuter du code Python directement dans votre navigateur.
Opérateurs mathématiques
Maintenant, nous allons voir comment définir le comportement de vos objets avec les symboles mathématiques :
-
__add__(self, other)pour l'opérateur+ -
__eq__(self, other)pour l'opérateur de comparaison== -
__lt__(self, other)pour l'opérateur "plus petit que"<
À noter
Note : Python propose de nombreuses autres méthodes spéciales pour les opérateurs (__sub__, __mul__, __div__, __gt__, __le__, __ge__, etc.).
class Score: def __init__(self, points): self.points = points def __add__(self, autre): return Score(self.points + autre.points) def __eq__(self, autre): return self.points == autre.points def __lt__(self, autre): return self.points < autre.points joueur1 = Score(100) joueur2 = Score(150) total = joueur1 + joueur2 print(total.points) # 250 print(joueur1 == joueur2) # False print(joueur1 < joueur2) # True
Inscrivez-vous gratuitement pour modifier et exécuter du code Python directement dans votre navigateur.
Imaginons une classe Panier où l'on souhaite additionner deux paniers et fusionner leur contenu :
class Panier: def __init__(self, articles): self.articles = articles def __add__(self, autre_panier): nouveaux_articles = self.articles + autre_panier.articles return Panier(nouveaux_articles) def __str__(self): return f"Panier contenant : {', '.join(self.articles)}" p1 = Panier(["Pomme", "Banane"]) p2 = Panier(["Orange"]) # Python détecte le symbole +, et appelle p1.__add__(p2) p3 = p1 + p2 print(p3) # Affiche : Panier contenant : Pomme, Banane, Orange
Inscrivez-vous gratuitement pour modifier et exécuter du code Python directement dans votre navigateur.
Pourquoi utiliser les dunder methods ?
En Python, nous sommes souvent amenés à créer nos propres objets. Les méthodes spéciales permettent de ne pas avoir à inventer de nouveaux noms de méthodes complexes en simplifiant votre code grâce à l'utilisation des fonctions et des opérateurs que tout le monde connaît déjà. De ce fait, votre code devient beaucoup plus intuitif pour vous et pour les autres.