Module : Méthodes Spéciales (Dunder Methods)
1. Quoi : Les Méthodes Spéciales
Les méthodes spéciales, aussi appelées "méthodes magiques" ou "dunder methods" (pour Double Underscore), sont des méthodes prédéfinies en Python que vous pouvez ajouter à vos classes pour qu'elles s'intègrent avec les fonctionnalités natives du langage.
Leur nom est toujours entouré de doubles underscores (ex: __init__, __str__, __len__). Vous en connaissez déjà une : __init__, le constructeur.
En implémentant ces méthodes, vous permettez à vos objets de se comporter comme des types de données intégrés, en répondant à des opérateurs (+, ==), des fonctions (len(), str()), et d'autres syntaxes.
2. Pourquoi : Rendre les objets plus "pythonic"
L'utilisation des méthodes spéciales rend vos objets plus intuitifs et plus faciles à utiliser.
print(my_object)peut afficher une description lisible au lieu de<__main__.MyClass object at 0x...>.len(my_collection)peut retourner le nombre d'éléments de votre objet collection personnalisé.obj1 + obj2peut effectuer une addition logique entre deux de vos objets.if my_object:peut évaluer la "vérité" de votre objet.
3. Comment : Les Méthodes Spéciales les plus courantes
A. Représentation d'objet : __str__ et __repr__
__str__(self): Doit retourner une chaîne de caractères lisible par l'humain. C'est ce qui est appelé parprint(obj)etstr(obj). Le but est l'affichage.__repr__(self): Doit retourner une représentation non ambiguë de l'objet, idéalement une chaîne qui pourrait être utilisée pour recréer l'objet (ex:MyClass(arg1=value1)). C'est ce qui est affiché dans la console interactive si vous tapez juste le nom de l'objet. C'est aussi le fallback si__str__n'est pas défini.
✅ Bonne pratique : Implémentez toujours __repr__. Implémentez __str__ si vous voulez une version plus "jolie" pour l'affichage.
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def __str__(self):
return f'"{self.title}" by {self.author}'
def __repr__(self):
return f"Book(title='{self.title}', author='{self.author}')"
book = Book("The Hobbit", "J.R.R. Tolkien")
print(book) # Appelle __str__ -> "The Hobbit" by J.R.R. Tolkien
print(str(book)) # Appelle __str__
print(repr(book)) # Appelle __repr__ -> Book(title='The Hobbit', author='J.R.R. Tolkien')
# Dans une console interactive, taper `book` appellerait __repr__
B. Comportement de collection : __len__ et __getitem__
__len__(self): Doit retourner la "longueur" de l'objet, un entier. Permet d'utiliser la fonctionlen(obj).__getitem__(self, key): Permet d'accéder à un élément via un index ou une clé, comme pour les listes ou les dictionnaires (obj[key]).
class Deck:
def __init__(self):
ranks = [str(n) for n in range(2, 11)] + list('JQKA')
suits = '♠♡♢♣'
self.cards = [f"{r}{s}" for s in suits for r in ranks]
def __len__(self):
return len(self.cards)
def __getitem__(self, position):
return self.cards[position]
deck = Deck()
print(len(deck)) # Appelle __len__ -> 52
print(deck[0]) # Appelle __getitem__(0) -> 2♠
print(deck[-1]) # Appelle __getitem__(-1) -> A♣
C. Opérateurs de comparaison : __eq__
__eq__(self, other): Définit le comportement de l'opérateur d'égalité==. Doit retournerTrueouFalse.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
# Deux points sont égaux si leurs coordonnées x et y sont égales
if not isinstance(other, Point):
return NotImplemented
return self.x == other.x and self.y == other.y
p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = Point(3, 4)
print(p1 == p2) # Appelle __eq__ -> True
print(p1 == p3) # Appelle __eq__ -> False
print(p1 == (1, 2)) # False (grâce à isinstance)
D'autres opérateurs existent : __ne__ (!=), __lt__ (<), __gt__ (>), etc.
D. Opérateurs arithmétiques : __add__
__add__(self, other): Définit le comportement de l'opérateur d'addition+.
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector({self.x}, {self.y})"
def __add__(self, other):
# L'addition de deux vecteurs est l'addition de leurs composantes
if not isinstance(other, Vector):
return NotImplemented
return Vector(self.x + other.x, self.y + other.y)
v1 = Vector(2, 4)
v2 = Vector(1, 3)
v3 = v1 + v2 # Appelle __add__
print(v3) # Vector(3, 7)
D'autres opérateurs existent : __sub__ (-), __mul__ (*), etc.
E. Valeur booléenne : __bool__
__bool__(self): Définit la valeur de vérité de l'objet lorsqu'il est utilisé dans un contexte booléen (if obj:). Doit retournerTrueouFalse. Si non défini, Python utilise__len__(un objet de longueur 0 estFalse).
class ShoppingCart:
def __init__(self):
self.items = []
def __bool__(self):
# Le panier est "vrai" s'il n'est pas vide
return len(self.items) > 0
cart = ShoppingCart()
if not cart: # Appelle __bool__
print("Your cart is empty.") # S'affiche
cart.items.append("apple")
if cart: # Appelle __bool__
print("You have items in your cart.") # S'affiche
Exercice 01 : Création d'une Classe Vector
Objectif
Cet exercice a pour but de vous faire implémenter plusieurs méthodes spéciales pour créer une classe Vector (vecteur 2D) qui se comporte de manière intuitive avec les opérateurs Python natifs.
Contexte
Vous allez créer une classe Vector qui représente un vecteur dans un plan 2D, avec des composantes x et y. Vous la rendrez plus "pythonic" en implémentant des méthodes spéciales pour :
- L'afficher de manière lisible (
__str__et__repr__). - L'additionner avec un autre vecteur (
__add__). - Tester son égalité avec un autre vecteur (
__eq__).
Énoncé
-
Créez un nouveau fichier Python nommé
vector_class.py. -
Définissez la classe
Vector.- Son constructeur
__init__doit accepterxetyet les stocker comme attributs.
- Son constructeur
-
Implémentez
__repr__(self):- Cette méthode doit retourner une chaîne de caractères qui pourrait être utilisée pour recréer l'objet.
- Exemple de retour :
Vector(x=3, y=4)
-
Implémentez
__str__(self):- Cette méthode doit retourner une représentation plus simple et lisible par un humain.
- Exemple de retour :
(3, 4)
-
Implémentez
__eq__(self, other):- Cette méthode définit le comportement de l'opérateur
==. - Elle doit retourner
Truesiotherest aussi une instance deVectorET que leurs composantesxetysont égales. Sinon, elle retourneFalse.
- Cette méthode définit le comportement de l'opérateur
-
Implémentez
__add__(self, other):- Cette méthode définit le comportement de l'opérateur
+. - L'addition de deux vecteurs
v1(x1, y1)etv2(x2, y2)est un nouveau vecteurv3(x1+x2, y1+y2). - La méthode doit vérifier si
otherest bien une instance deVector. Si ce n'est pas le cas, elle doit retournerNotImplemented(une constante spéciale qui indique à Python que l'opération n'est pas supportée entre ces types). - Si
otherest unVector, la méthode doit retourner une nouvelle instance deVectorreprésentant la somme.
- Cette méthode définit le comportement de l'opérateur
-
Testez votre classe :
- Créez deux vecteurs
v1 = Vector(2, 3)etv2 = Vector(5, 1). - Testez
print(),str(), etrepr(). - Testez l'addition
v3 = v1 + v2et affichezv3. - Testez l'égalité
v1 == v2etv1 == Vector(2, 3).
- Créez deux vecteurs
Résultat Attendu
v1 string representation: (2, 3)
v1 official representation: Vector(x=2, y=3)
v3 = v1 + v2
v3 string representation: (7, 4)
v1 == v2: False
v1 == Vector(2, 3): True
v1 == (2, 3): False
Cliquez ici pour voir un exemple de code de solution
# vector_class.py
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
"""Official representation, good for debugging."""
return f"Vector(x={self.x}, y={self.y})"
def __str__(self):
"""User-friendly string representation."""
return f"({self.x}, {self.y})"
def __eq__(self, other):
"""Equality comparison (==)."""
if not isinstance(other, Vector):
return False
return self.x == other.x and self.y == other.y
def __add__(self, other):
"""Addition (+)."""
if not isinstance(other, Vector):
return NotImplemented
# Return a new Vector instance
return Vector(self.x + other.x, self.y + other.y)
# --- Testing ---
v1 = Vector(2, 3)
v2 = Vector(5, 1)
# Test __str__ and __repr__
print(f"v1 string representation: {str(v1)}")
print(f"v1 official representation: {repr(v1)}")
print("-" * 20)
# Test __add__
print("v3 = v1 + v2")
v3 = v1 + v2
print(f"v3 string representation: {v3}")
print("-" * 20)
# Test __eq__
print(f"v1 == v2: {v1 == v2}")
print(f"v1 == Vector(2, 3): {v1 == Vector(2, 3)}")
print(f"v1 == (2, 3): {v1 == (2, 3)}") # Should be False thanks to isinstance check