Qu'est-ce que __repr__ en Python ?

Découvrez la méthode spéciale __repr__ en Python : définition, différences avec __str__, exemples pratiques et bonnes pratiques pour représenter vos objets.
9 min de lecture
Believemy logo

Définition de __repr__ en Python

__repr__ est une méthode spéciale (aussi appelée dunder method ou magic method) en Python qui permet de définir la représentation officielle d'un objet sous forme de chaîne de caractères. Son nom vient de l'anglais representation. Lorsque vous appelez la fonction native repr() sur un objet ou que vous inspectez un objet directement dans un interpréteur Python, c'est la méthode __repr__ qui est invoquée en arrière-plan.

L'objectif principal de __repr__ est de fournir une représentation non ambiguë de l'objet, idéalement suffisamment précise pour qu'un développeur puisse recréer l'objet à partir de cette chaîne. Si vous souhaitez approfondir vos connaissances sur Python et maîtriser les méthodes spéciales, nous vous recommandons de suivre notre formation complète sur Python qui couvre en détail la programmation orientée objet.

Bon à savoir

La règle d'or : __repr__ est destinée aux développeurs, tandis que __str__ est destinée aux utilisateurs finaux. Si vous ne devez implémenter qu'une seule des deux méthodes, choisissez toujours __repr__.

 

Comment fonctionne __repr__ ?

Chaque objet en Python hérite de la class de base object, qui fournit une implémentation par défaut de __repr__. Cette implémentation par défaut retourne une chaîne contenant le nom de la classe et l'adresse mémoire de l'objet, ce qui n'est généralement pas très utile :

PYTHON
class Voiture:
    def __init__(self, marque, modele):
        self.marque = marque
        self.modele = modele

ma_voiture = Voiture("Peugeot", "308")
print(repr(ma_voiture))
# Résultat : <__main__.Voiture object at 0x7f8b8c0d4a90>

Comme vous pouvez le constater, cette sortie par défaut ne nous dit rien sur le contenu réel de l'objet. C'est pourquoi il est fortement recommandé de définir votre propre méthode __repr__ dans vos classes personnalisées.

 

Implémentation personnalisée de __repr__

Voici comment implémenter correctement __repr__ pour notre classe Voiture :

PYTHON
class Voiture:
    def __init__(self, marque, modele, annee):
        self.marque = marque
        self.modele = modele
        self.annee = annee

    def __repr__(self):
        return f"Voiture(marque='{self.marque}', modele='{self.modele}', annee={self.annee})"

ma_voiture = Voiture("Peugeot", "308", 2023)
print(repr(ma_voiture))
# Résultat : Voiture(marque='Peugeot', modele='308', annee=2023)

Remarquez que nous utilisons ici une f-string pour formater la sortie. L'idée est que la chaîne retournée ressemble à l'appel du constructeur qui permettrait de recréer l'objet.

 

Différence entre __repr__ et __str__

La distinction entre __repr__ et __str__ est une source fréquente de confusion pour les développeurs. Voici un tableau récapitulatif pour bien comprendre leurs différences :

Caractéristique__repr____str__
Public cibleDéveloppeursUtilisateurs finaux
ObjectifReprésentation non ambiguëReprésentation lisible
Appelée parrepr(), interpréteur interactifstr(), print()
FallbackImplémentation par défaut de objectUtilise __repr__ si non définie
ConventionDevrait ressembler à du code Python valideLibre, axée sur la lisibilité
Attention

Point important : si vous définissez __repr__ mais pas __str__, Python utilisera automatiquement __repr__ comme fallback lors de l'appel à print ou str(). L'inverse n'est pas vrai.

Voici un exemple concret illustrant les deux méthodes :

PYTHON
class Produit:
    def __init__(self, nom, prix):
        self.nom = nom
        self.prix = prix

    def __repr__(self):
        return f"Produit(nom='{self.nom}', prix={self.prix})"

    def __str__(self):
        return f"{self.nom} - {self.prix}€"

produit = Produit("Clavier mécanique", 89.99)

# __repr__ est appelée dans l'interpréteur ou via repr()
print(repr(produit))  # Produit(nom='Clavier mécanique', prix=89.99)

# __str__ est appelée par print() ou str()
print(produit)        # Clavier mécanique - 89.99€

 

Quand __repr__ est-elle appelée ?

La méthode __repr__ est invoquée dans plusieurs contextes que vous devez connaître :

1. Dans l'interpréteur interactif

Lorsque vous tapez le nom d'un objet directement dans l'interpréteur Python (ou dans un notebook Jupyter), c'est __repr__ qui est appelée :

PYTHON
>>> produit = Produit("Souris", 49.99)
>>> produit
Produit(nom='Souris', prix=49.99)

 

2. Dans les conteneurs (listes, dictionnaires, etc.)

Quand un objet se trouve à l'intérieur d'un conteneur comme une list ou un dict, Python utilise __repr__ pour l'afficher, même si vous appelez print sur le conteneur :

PYTHON
produits = [
    Produit("Clavier", 89.99),
    Produit("Souris", 49.99),
    Produit("Écran", 299.99)
]

print(produits)
# [Produit(nom='Clavier', prix=89.99), Produit(nom='Souris', prix=49.99), Produit(nom='Écran', prix=299.99)]

C'est un comportement extrêmement important à comprendre : même si vos objets ont un __str__ très lisible, c'est __repr__ qui sera utilisée lorsqu'ils sont à l'intérieur d'une liste ou d'un dictionnaire.

 

3. Avec la fonction repr()

La fonction native repr() appelle directement __repr__ :

PYTHON
produit = Produit("Webcam", 59.99)
repr_str = repr(produit)
print(repr_str)  # Produit(nom='Webcam', prix=59.99)

 

4. Dans les f-strings avec le flag !r

Vous pouvez forcer l'utilisation de __repr__ dans une f-string grâce au flag !r :

PYTHON
produit = Produit("Casque", 129.99)
print(f"Debug : {produit!r}")
# Debug : Produit(nom='Casque', prix=129.99)

print(f"Affichage : {produit!s}")
# Affichage : Casque - 129.99€

 

Exemples avancés

__repr__ avec l'héritage

Lorsque vous travaillez avec l'héritage de classes, il est important de définir __repr__ correctement pour chaque niveau de la hiérarchie :

PYTHON
class Animal:
    def __init__(self, nom, espece):
        self.nom = nom
        self.espece = espece

    def __repr__(self):
        return f"Animal(nom='{self.nom}', espece='{self.espece}')"

class Chien(Animal):
    def __init__(self, nom, race):
        super().__init__(nom, espece="Chien")
        self.race = race

    def __repr__(self):
        return f"Chien(nom='{self.nom}', race='{self.race}')"

chien = Chien("Rex", "Berger Allemand")
print(repr(chien))  # Chien(nom='Rex', race='Berger Allemand')

 

__repr__ avec les dataclasses

Les dataclass en Python génèrent automatiquement une méthode __repr__ pour vous, ce qui est l'un de leurs grands avantages :

PYTHON
from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float
    z: float = 0.0

point = Point(1.5, 2.3, 4.0)
print(repr(point))  # Point(x=1.5, y=2.3, z=4.0)

Comme vous le voyez, la dataclass génère un __repr__ parfaitement conforme aux conventions. Vous pouvez toutefois désactiver ce comportement avec @dataclass(repr=False) si vous souhaitez définir votre propre implémentation.

 

__repr__ avec les namedtuples

De la même manière, les namedtuple fournissent automatiquement un __repr__ informatif :

PYTHON
from collections import namedtuple

Couleur = namedtuple("Couleur", ["rouge", "vert", "bleu"])
c = Couleur(255, 128, 0)
print(repr(c))  # Couleur(rouge=255, vert=128, bleu=0)

 

__repr__ et eval()

L'une des conventions les plus importantes de __repr__ est que, dans la mesure du possible, la chaîne retournée devrait être un code Python valide permettant de recréer l'objet :

PYTHON
class Vecteur:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Vecteur({self.x}, {self.y})"

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

v1 = Vecteur(3, 4)
v2 = eval(repr(v1))  # Recrée l'objet à partir de sa repr

print(v1 == v2)  # True
print(repr(v2))  # Vecteur(3, 4)
Attention

Attention : n'utilisez jamais eval() sur des données non fiables en production. Cet exemple est purement illustratif de la convention __repr__.

 

Bonnes pratiques

Voici les règles essentielles à suivre lorsque vous implémentez __repr__ dans vos classes :

1. Toujours implémenter __repr__

Même si vous définissez __str__, implémentez toujours __repr__. C'est la méthode de représentation la plus fondamentale et elle sert de fallback pour __str__.

 

2. Retourner du code Python valide si possible

La convention veut que __repr__ retourne une chaîne qui, passée à eval(), recréerait l'objet :

PYTHON
# Bonne pratique
def __repr__(self):
    return f"MaClasse(param1={self.param1!r}, param2={self.param2!r})"

# Mauvaise pratique
def __repr__(self):
    return f"Instance de MaClasse"
Bon à savoir

Notez l'utilisation de !r dans la f-string pour les attributs de type chaîne. Cela garantit que les guillemets sont inclus dans la sortie, rendant la représentation plus fidèle.

 

3. Utiliser le format <...> quand eval() n'est pas possible

Si votre objet est trop complexe pour être recréé via eval(), utilisez la convention des chevrons :

PYTHON
class ConnexionBDD:
    def __init__(self, hote, port, base):
        self.hote = hote
        self.port = port
        self.base = base
        self._connexion = None  # État interne complexe

    def __repr__(self):
        return f""

conn = ConnexionBDD("localhost", 5432, "mon_app")
print(repr(conn))  # 

 

4. Inclure les informations essentielles

La représentation doit contenir suffisamment d'informations pour identifier l'objet et le distinguer des autres instances. Incluez au minimum les attributs définis dans le constructeur (__init__ via def).

 

5. Garder la représentation concise

Évitez d'inclure des attributs internes ou des détails d'implémentation qui ne sont pas pertinents pour le débogage :

PYTHON
# Trop verbeux
def __repr__(self):
    return (f"Utilisateur(id={self.id}, nom='{self.nom}', email='{self.email}', "
            f"date_creation={self.date_creation}, derniere_connexion={self.derniere_connexion}, "
            f"nb_connexions={self.nb_connexions}, preferences={self.preferences})")

# Juste ce qu'il faut
def __repr__(self):
    return f"Utilisateur(id={self.id}, nom='{self.nom}', email='{self.email}')"

 

__repr__ pour les types natifs de Python

Il est intéressant de noter que tous les types natifs de Python implémentent déjà __repr__ de manière cohérente :

PYTHON
# Chaînes de caractères
print(repr("Bonjour"))       # 'Bonjour'

# Nombres
print(repr(42))              # 42
print(repr(3.14))            # 3.14

# Listes
print(repr([1, 2, 3]))       # [1, 2, 3]

# Tuples
print(repr((1, 2)))          # (1, 2)

# Dictionnaires
print(repr({"a": 1}))        # {'a': 1}

# Ensembles
print(repr({1, 2, 3}))       # {1, 2, 3}

Vous remarquerez que pour les chaînes de caractères, repr() ajoute des guillemets autour de la valeur, ce qui la distingue clairement de str(). Cela est particulièrement utile pour le débogage lorsque vous travaillez avec des tuple, des sets ou des dict.

 

Questions fréquentes

Question

Quelle est la différence entre __repr__ et __str__ en Python ?

__repr__ fournit une représentation technique et non ambiguë de l'objet, destinée aux développeurs. Elle doit idéalement retourner du code Python valide permettant de recréer l'objet. __str__, en revanche, fournit une représentation lisible et conviviale, destinée aux utilisateurs finaux. Si __str__ n'est pas définie, Python utilise __repr__ comme fallback. La convention recommande de toujours définir __repr__ en priorité.

 

Question

Dois-je toujours implémenter __repr__ dans mes classes ?

Oui, c'est une bonne pratique fortement recommandée. Sans implémentation personnalisée, Python utilise la représentation par défaut de object qui affiche uniquement le nom de la classe et l'adresse mémoire, ce qui n'est pas utile pour le débogage. Si vous utilisez des dataclass ou des namedtuple, la méthode __repr__ est générée automatiquement, ce qui vous évite de l'écrire manuellement.

 

Question

Pourquoi __repr__ est-elle utilisée à l'intérieur des listes et dictionnaires ?

Lorsque Python affiche le contenu d'un conteneur (list, dict, tuple, etc.), il utilise __repr__ et non __str__ pour chaque élément. Ce choix de conception s'explique par le fait que l'affichage d'un conteneur est généralement destiné au débogage, et Python privilégie donc la représentation non ambiguë. C'est pourquoi il est essentiel d'avoir un __repr__ informatif pour bien visualiser vos objets.

 

Question

Comment apprendre à maîtriser les méthodes spéciales en Python ?

Les méthodes spéciales comme __repr__, __str__, __init__ ou encore __eq__ sont au cœur de la programmation orientée objet en Python. Pour les maîtriser en profondeur, nous vous recommandons de suivre notre formation dédiée à Python sur Believemy, qui couvre de manière progressive et pratique toutes ces notions essentielles, des bases jusqu'aux concepts avancés.

Termes connexes

Découvrez notre glossaire Python

Parcourez les termes et définitions les plus couramment utilisés dans le domaine du développement avec Python.

Partager cet article

Tu veux nous aider ? Fais un lien vers cet article sur tes réseaux ou encore mieux : sur ton site, dans un article ou dans ta newsletter.