Leçons du module (3/4)
Méthodes spéciales (dunder)
Les méthodes spéciales (appelées méthodes dunder parce qu'elles commencent et se terminent
par un double trait de soulignement : __name__) sont les crochets (hooks) qui permettent à vos objets
de s'intégrer au langage : représentation textuelle, comparaison avec ==,
prise en charge de len(), in, opérateurs, etc.
Vous en avez déjà utilisé une : __init__.
__str__ et __repr__
Elles contrôlent ce qui apparaît avec print() et dans le REPL.
class Punto:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Punto({self.x}, {self.y})"
def __str__(self):
return f"({self.x}, {self.y})"
p = Punto(3, 4)
str(p) # '(3, 4)' — pour les humains
repr(p) # 'Punto(3, 4)' — pour le débogueur / dev
print(p) # '(3, 4)'
p # Punto(3, 4) dans le REPL → repr
[p, p] # [Punto(3, 4), Punto(3, 4)] — toujours repr dans les conteneursSi vous ne définissez que __repr__, Python l'utilise également comme solution de repli pour
__str__.
__eq__ : redéfinir ==
Par défaut, a == b compare les identifiants des objets (is), pas
leur contenu. Pour une comparaison structurelle, définissez __eq__ :
class Punto:
def __init__(self, x, y):
self.x, self.y = x, y
def __eq__(self, other):
if not isinstance(other, Punto):
return NotImplemented
return (self.x, self.y) == (other.x, other.y)
Punto(1, 2) == Punto(1, 2) # True
Punto(1, 2) == Punto(3, 4) # FalseRenvoyer NotImplemented (un mot-clé spécial, pas False !) lorsque les types
ne sont pas comparables est la bonne pratique : Python essaiera alors avec le
__eq__ de l'autre opérande.
__len__ : prise en charge de len()
class Cesto:
def __init__(self):
self.frutti = []
def __len__(self):
return len(self.frutti)
c = Cesto()
c.frutti.append("mela")
len(c) # 1
bool(c) # True (len > 0)En définissant __len__, vous obtenez également le booléen gratuitement : un objet
avec len() == 0 devient évalué à faux (falsy).
Autres dunders utiles (aperçu)
__add__(self, other)→ redéfinit+__lt__,__le__,__gt__,__ge__→ opérateurs de comparaison__contains__(self, item)→ prend en chargeitem in self__iter__+__next__→ rend l'objet itérable__getitem__(self, key)→ prend en chargeself[key]__hash__→ requis pour être une clé de dictionnaire / un élément de set
À vous de jouer
Définissez `Money` avec __init__(self, amount, currency) et __str__ qui renvoie `f'{amount} {currency}'`. Créez `s = Money(42, 'EUR')` et évaluez `str(s)`.
Afficher l'indice
f-string dans __str__.
Solution disponible après 3 tentatives
Exercice de révision
Définissez `Pair` avec __init__(self, a, b) et __eq__ basé sur la comparaison structurelle de (a, b). Évaluez `Pair(1, 2) == Pair(1, 2)`.
Afficher l'indice
Comparez les tuples (self.a, self.b) == (other.a, other.b).
Solution disponible après 3 tentatives
Défi supplémentaire
Définissez une classe `Book` avec un constructeur prenant `title` et `author`. Définissez la méthode spéciale `__str__(self)` pour qu'elle renvoie la chaîne `"Title by Author"` (par exemple `'1984 by George Orwell'`). Instanciez un livre et évaluez `str(book)`.
Afficher l'indice
Utilisez f"{self.title} by {self.author}" à l'intérieur de def __str__(self):.
Solution disponible après 3 tentatives