Passer au contenu principal
eLearner.app
Module 7 · Leçon 3 sur 427/36 dans le cours~12 min
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.

Python
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 conteneurs

Si 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__ :

Python
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)    # False

Renvoyer 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()

Python
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 charge item in self
  • __iter__ + __next__ → rend l'objet itérable
  • __getitem__(self, key) → prend en charge self[key]
  • __hash__ → requis pour être une clé de dictionnaire / un élément de set

À vous de jouer

Exercice#python.m7.l3.e1
Tentatives : 0Chargement…

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)`.

Chargement de l'éditeur…
Afficher l'indice

f-string dans __str__.

Solution disponible après 3 tentatives

Exercice de révision

Exercice#python.m7.l3.e2
Tentatives : 0Chargement…

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)`.

Chargement de l'éditeur…
Afficher l'indice

Comparez les tuples (self.a, self.b) == (other.a, other.b).

Solution disponible après 3 tentatives

Défi supplémentaire

Exercice#python.m7.l3.e3
Tentatives : 0Chargement…

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)`.

Chargement de l'éditeur…
Afficher l'indice

Utilisez f"{self.title} by {self.author}" à l'intérieur de def __str__(self):.

Solution disponible après 3 tentatives