Définition de __str__ en Python
La méthode __str__ est une méthode spéciale (aussi appelée dunder method ou magic method) en Python qui permet de définir la représentation en chaîne de caractères d'un objet. Lorsque vous utilisez la fonction print sur un objet ou que vous appelez str(), Python invoque automatiquement la méthode __str__ de cet objet pour obtenir une représentation lisible et compréhensible par un humain.
En d'autres termes, __str__ vous permet de contrôler ce qui s'affiche lorsqu'un utilisateur demande à voir votre objet sous forme de texte. C'est un concept fondamental de la programmation orientée objet en Python, que vous pouvez approfondir dans notre formation complète sur Python.
Concrètement, sans __str__, l'affichage d'un objet personnalisé ressemble à ceci :
class Utilisateur:
def __init__(self, nom, age):
self.nom = nom
self.age = age
u = Utilisateur("Alice", 30)
print(u)
# Résultat : <__main__.Utilisateur object at 0x7f8b8c0d4a90>Ce résultat n'est pas très parlant. Grâce à __str__, vous pouvez transformer cet affichage en quelque chose de bien plus utile et lisible.
Syntaxe et fonctionnement de __str__
La méthode __str__ se définit à l'intérieur d'une class Python. Elle prend uniquement self comme paramètre et doit obligatoirement retourner une chaîne de caractères (str).
class Utilisateur:
def __init__(self, nom, age):
self.nom = nom
self.age = age
def __str__(self):
return f"{self.nom}, {self.age} ans"
u = Utilisateur("Alice", 30)
print(u)
# Résultat : Alice, 30 ans__str__ doit toujours retourner un objet de type str. Si vous retournez un autre type (un entier, une liste, etc.), Python lèvera une erreur TypeError.
Voici les situations dans lesquelles Python appelle automatiquement __str__ :
| Contexte d'appel | Exemple | Description |
|---|---|---|
print() | print(objet) | Affiche la représentation textuelle de l'objet |
str() | str(objet) | Convertit l'objet en chaîne de caractères |
| f-string | f"{objet}" | Insère la représentation dans une f-string |
format() | "{}".format(objet) | Utilisé dans le formatage de chaînes |
Différence entre __str__ et __repr__
L'une des questions les plus fréquentes en Python concerne la différence entre __str__ et __repr__. Ces deux méthodes servent à représenter un objet sous forme de texte, mais elles ont des objectifs fondamentalement différents.
| Critère | __str__ | __repr__ |
|---|---|---|
| Public cible | Utilisateur final | Développeur |
| Objectif | Lisibilité | Précision et non-ambiguïté |
| Appelé par | print(), str() | repr(), console interactive |
| Fallback | Appelle __repr__ si absent | Affiche l'adresse mémoire si absent |
Voici un exemple concret montrant cette différence :
class Produit:
def __init__(self, nom, prix):
self.nom = nom
self.prix = prix
def __str__(self):
return f"{self.nom} — {self.prix} €"
def __repr__(self):
return f"Produit(nom='{self.nom}', prix={self.prix})"
p = Produit("Clavier mécanique", 89.99)
print(str(p)) # Clavier mécanique — 89.99 €
print(repr(p)) # Produit(nom='Clavier mécanique', prix=89.99)Si vous ne définissez que __repr__ sans __str__, Python utilisera __repr__ comme fallback lorsque __str__ est appelé. L'inverse n'est pas vrai : si seul __str__ est défini, repr() n'utilisera pas __str__.
En règle générale, nous vous recommandons de toujours définir __repr__ en priorité (pour le débogage) et d'ajouter __str__ lorsque vous souhaitez un affichage plus convivial pour l'utilisateur final.
Exemples pratiques
Représenter un objet simple
Commençons par un exemple basique avec une classe représentant un livre :
class Livre:
def __init__(self, titre, auteur, pages):
self.titre = titre
self.auteur = auteur
self.pages = pages
def __str__(self):
return f"'{self.titre}' par {self.auteur} ({self.pages} pages)"
livre = Livre("Le Petit Prince", "Saint-Exupéry", 96)
print(livre)
# 'Le Petit Prince' par Saint-Exupéry (96 pages)
Utiliser __str__ dans une collection d'objets
Lorsque vous placez des objets dans une list ou un dict, le comportement diffère. À l'intérieur d'une collection, Python utilise __repr__ et non __str__. Voici comment gérer cela :
class Etudiant:
def __init__(self, nom, moyenne):
self.nom = nom
self.moyenne = moyenne
def __str__(self):
return f"{self.nom} (moyenne : {self.moyenne}/20)"
def __repr__(self):
return self.__str__()
etudiants = [
Etudiant("Alice", 15.5),
Etudiant("Bob", 12.0),
Etudiant("Charlie", 17.8)
]
# Affichage individuel → utilise __str__
for e in etudiants:
print(e)
# Alice (moyenne : 15.5/20)
# Bob (moyenne : 12.0/20)
# Charlie (moyenne : 17.8/20)
# Affichage de la liste → utilise __repr__
print(etudiants)
# [Alice (moyenne : 15.5/20), Bob (moyenne : 12.0/20), Charlie (moyenne : 17.8/20)]
__str__ avec l'héritage de classes
La méthode __str__ peut être héritée et surchargée dans les classes enfants. C'est un mécanisme puissant du polymorphisme en Python :
class Animal:
def __init__(self, nom, espece):
self.nom = nom
self.espece = espece
def __str__(self):
return f"{self.nom} est un(e) {self.espece}"
class Chien(Animal):
def __init__(self, nom, race):
super().__init__(nom, "chien")
self.race = race
def __str__(self):
return f"{super().__str__()} de race {self.race}"
class Chat(Animal):
def __init__(self, nom, interieur=True):
super().__init__(nom, "chat")
self.interieur = interieur
# Pas de __str__ défini → utilise celui du parent
rex = Chien("Rex", "Berger Allemand")
minou = Chat("Minou")
print(rex) # Rex est un(e) chien de race Berger Allemand
print(minou) # Minou est un(e) chat
__str__ avec les dataclasses
Les dataclass génèrent automatiquement une méthode __repr__, mais pas de __str__ personnalisée. Si vous souhaitez un affichage différent de celui par défaut, vous devez la définir manuellement :
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
def __str__(self):
return f"Point({self.x}, {self.y})"
p = Point(3.5, 7.2)
print(p) # Point(3.5, 7.2)
print(repr(p)) # Point(x=3.5, y=7.2) ← généré automatiquement
Exemple avancé : affichage formaté multi-lignes
Rien ne vous empêche de retourner une chaîne multi-lignes dans __str__. C'est particulièrement utile pour les objets complexes :
class Facture:
def __init__(self, numero, client, lignes):
self.numero = numero
self.client = client
self.lignes = lignes # liste de tuples (description, montant)
def total(self):
return sum(montant for _, montant in self.lignes)
def __str__(self):
en_tete = f"Facture #{self.numero} — Client : {self.client}"
separateur = "-" * 45
lignes_texte = "\n".join(
f" {desc:<30} {montant:>8.2f} €"
for desc, montant in self.lignes
)
total_texte = f" {'TOTAL':<30} {self.total():>8.2f} €"
return f"{en_tete}\n{separateur}\n{lignes_texte}\n{separateur}\n{total_texte}"
facture = Facture("2024-001", "Believemy", [
("Formation Python", 299.00),
("Support premium", 49.99),
("Certificat", 19.99)
])
print(facture)
# Facture #2024-001 — Client : Believemy
# ---------------------------------------------
# Formation Python 299.00 €
# Support premium 49.99 €
# Certificat 19.99 €
# ---------------------------------------------
# TOTAL 368.98 €
Bonnes pratiques
Voici les règles essentielles à suivre lorsque vous implémentez __str__ dans vos classes Python :
- Retournez toujours un
str: C'est une obligation. Tout autre type provoquera une erreurTypeError. - Gardez le message concis et lisible :
__str__est destiné à l'utilisateur final, pas au développeur. Évitez les détails techniques superflus. - Définissez aussi
__repr__: Ne comptez pas uniquement sur__str__. Implémentez toujours__repr__pour le débogage. - Évitez les effets de bord : La méthode
__str__ne devrait jamais modifier l'état de l'objet. Elle doit être une opération en lecture seule. - Utilisez les f-string : Elles sont le moyen le plus lisible et le plus performant pour construire la chaîne de retour.
- Ne levez pas d'exception :
__str__devrait toujours réussir. Gérez les cas limites (attributsNone, listes vides, etc.) avec des valeurs par défaut. - Pensez à l'héritage : Si votre classe est destinée à être héritée, concevez
__str__de manière à ce qu'elle puisse être étendue viasuper().__str__().
Une bonne règle à retenir : __str__ répond à la question « Comment présenter cet objet à un utilisateur ? » tandis que __repr__ répond à « Comment recréer cet objet ? ».
Erreurs courantes à éviter
Voici les pièges les plus fréquents rencontrés par les développeurs Python lorsqu'ils travaillent avec __str__ :
1. Oublier de retourner une chaîne
# ❌ Mauvais : print au lieu de return
class Mauvais:
def __str__(self):
print("Mon objet") # Ne retourne rien (None)
# ✅ Bon : retourner la chaîne
class Bon:
def __str__(self):
return "Mon objet"
2. Retourner un type incorrect
# ❌ Mauvais : retourne un entier
class Compteur:
def __init__(self, valeur):
self.valeur = valeur
def __str__(self):
return self.valeur # TypeError si valeur n'est pas str !
# ✅ Bon : convertir en chaîne
class Compteur:
def __init__(self, valeur):
self.valeur = valeur
def __str__(self):
return str(self.valeur)
3. Confondre __str__ et __repr__ dans les collections
class Element:
def __init__(self, nom):
self.nom = nom
def __str__(self):
return self.nom
# __repr__ non défini !
elements = [Element("A"), Element("B"), Element("C")]
print(elements)
# [<__main__.Element object at 0x...>, ...]
# __str__ n'est PAS utilisé dans les listes !
Questions fréquentes
Quelle est la différence entre __str__ et __repr__ en Python ?
__str__ est conçue pour produire une représentation lisible pour l'utilisateur final, tandis que __repr__ vise une représentation non ambiguë pour le développeur, idéalement capable de recréer l'objet. Lorsque vous appelez print ou str(), c'est __str__ qui est invoquée. Dans la console interactive ou via repr(), c'est __repr__. Si __str__ n'est pas définie, Python utilise __repr__ comme fallback.
Faut-il toujours définir __str__ dans ses classes Python ?
Ce n'est pas obligatoire, mais c'est fortement recommandé dès que vos objets sont susceptibles d'être affichés. Au minimum, vous devriez toujours définir __repr__ pour faciliter le débogage. Ajoutez __str__ lorsque vous avez besoin d'un affichage convivial différent de la représentation technique. Pour les dataclass, __repr__ est générée automatiquement, ce qui suffit souvent.
Peut-on utiliser __str__ avec les types natifs de Python ?
Oui, tous les types natifs de Python (int, float, list, dict, tuple, etc.) possèdent déjà une implémentation de __str__. C'est d'ailleurs grâce à cela que print([1, 2, 3]) affiche [1, 2, 3] au lieu de l'adresse mémoire de l'objet liste. Vous ne pouvez pas modifier __str__ sur les types natifs directement, mais vous pouvez créer des sous-classes si nécessaire.
Comment apprendre à maîtriser __str__ et les méthodes spéciales Python ?
Les méthodes spéciales comme __str__, __repr__, __init__, __eq__ ou encore __len__ (liée à len) sont au cœur de la programmation orientée objet en Python. Pour les maîtriser pleinement, nous vous recommandons de suivre notre formation dédiée à Python sur Believemy, qui couvre en détail la création de classes, les méthodes spéciales et les bonnes pratiques de développement Python.