À quoi sert __str__ ?

La méthode spéciale __str__ permet d'indiquer la représentation en chaîne de caractères d'un objet.

Cette chaîne de caractères est retournée lors de l'utilisation de la fonction str sur un objet ou lors de l'utilisation de la fonction print :

import datetime
now = datetime.datetime.now()

print(now)
print(str(now))

Pour implémenter cette méthode, il suffit de la surcharger à l'intérieur d'une classe :

class Utilisateur:
    def __init__(self, prenom, nom):
        self.prenom = prenom
        self.nom = nom

    def __str__(self):
        return self.prenom + " " + self.nom

utilisateur_01 = Utilisateur(prenom="Patrick", nom="Smith")
print(utilisateur_01)
print(str(utilisateur_01))

Cette méthode doit obligatoirement retourner une chaîne de caractères.

Si vous n'implémentez pas cette méthode dans votre classe, la méthode __repr__ sera utilisée à la place.

class Utilisateur:
    def __init__(self, prenom, nom):
        self.prenom = prenom
        self.nom = nom

    # On implémente la méthode __repr__ mais pas __str__
    def __repr__(self):
        return "Utilisateur(prenom='{}', nom='{}')".format(self.prenom, self.nom)

utilisateur_01 = Utilisateur(prenom="Patrick", nom="Smith")

"""Quand on fait un print de l'objet ou qu'on utilise la fonction str,
le résultat de __repr__ nous est retourné"""
print(utilisateur_01)
print(str(utilisateur_01))

Si vous n'avez pas implémenté la méthode __repr__, la représentation par défaut de la classe object (dont toutes les classes héritent) sera utilisée :

# On n'implémente ni __repr__ ni __str__
class Utilisateur:
    def __init__(self, prenom, nom):
        self.prenom = prenom
        self.nom = nom

utilisateur_01 = Utilisateur(prenom="Patrick", nom="Smith")

# Quand on utilise str sur notre instance, la classe et l'adresse en mémoire de l'instance sont affichées
print(utilisateur_01)                   # <__main__.Utilisateur object at 0x10f5952e0>
print(str(utilisateur_01))              # <__main__.Utilisateur object at 0x10f5952e0>

# Cette implémentation provient de la classe object
print(object.__repr__(utilisateur_01))  # <__main__.Utilisateur object at 0x10f5952e0>

Si vous exécutez le code ci-dessus, l'adresse en mémoire n'est pas affichée car le code est exécuté dans votre navigateur internet.

Pour résumer tous les cas de figure possibles :

class Bonjour:
    def __repr__(self):
        return "repr de Bonjour"

    def __str__(self):
        return "str de Bonjour"

b = Bonjour()
print("Avec __repr__ et __str__ :")
print("print(b) -> ", b)
print("repr(b)  -> ", repr(b))
print("str(b)   -> ", str(b))
print("-"*50)

class Bonjour:
    def __str__(self):
        return "str de Bonjour"

b = Bonjour()
print("Avec __str__ uniquement :")
print("print(b) -> ", b)
print("repr(b)  -> ", repr(b))
print("str(b)   -> ", str(b))
print("-"*50)

class Bonjour:
    def __repr__(self):
        return "repr de Bonjour"

b = Bonjour()
print("Avec __repr__ uniquement :")
print("print(b) -> ", b)
print("repr(b)  -> ", repr(b))
print("str(b)   -> ", str(b))
print("-"*50)

class Bonjour:
    pass

b = Bonjour()
print("Avec aucune implémentation :")
print("print(b) -> ", b)
print("repr(b)  -> ", repr(b))
print("str(b)   -> ", str(b))