__bool__

Qu'est-ce que la méthode __bool__ en Python ?

Qu'est-ce que la méthode __bool__ ?

Tout objet possède une valeur dite de vérité en Python. En fait, dès qu'on le place dans un if, un while ou encore un appel à bool(), il sera considéré comme vrai ou faux. __bool__ est la méthode spéciale qui permet à vos propres classes de déterminer cette valeur.

class Panier:
    def __init__(self, articles):
        self.articles = articles

    def __bool__(self):
        return len(self.articles) > 0


panier_patrick = Panier([])
panier_sebastien = Panier(["clavier", "souris"])

print(bool(panier_patrick))    # False
print(bool(panier_sebastien))  # True

if panier_sebastien:
    print("Le panier contient des articles")  # Le panier contient des articles
PYTHON
Un instant

Créez un compte pour exécuter ce code

Inscrivez-vous gratuitement pour modifier et exécuter du code Python directement dans votre navigateur.

Vous n'appelez jamais __bool__ directement : Python s'en charge pour vous. Voici quelques cas :

  • if objet / while objet

  • bool(objet)

  • objet and autre_objet

  • "patrick" if objet else "sebastien"

  • any(elements) / all(elements)

Comment Python détermine-t-il si un objet est vrai ou faux ?

Python suit un ordre précis pour déterminer si un objet est vrai ou faux :

  1. Si la classe définit __bool__, Python appelle cette méthode

  2. Sinon, si la classe possède la méthode __len__, c'est elle qui sera utilisée

  3. Si aucune des méthodes n'est implémentée, l'objet sera toujours vrai

class Panier:
    def __init__(self, articles):
        self.articles = articles


class PanierAvecLen:
    def __init__(self, articles):
        self.articles = articles

    def __len__(self):
        return len(self.articles)


class PanierAvecBool:
    def __init__(self, articles):
        self.articles = articles

    def __bool__(self):
        return bool(self.articles)


print(bool(Panier([])))          # True
print(bool(PanierAvecLen([])))   # False
print(bool(PanierAvecBool([])))  # False
PYTHON
Un instant

Créez un compte pour exécuter ce code

Inscrivez-vous gratuitement pour modifier et exécuter du code Python directement dans votre navigateur.

Comment définir __bool__ dans une classe ?

La méthode ne prend que self et renvoie un booléen. Si votre classe contient une collection, le comportement le plus banal est de laisser la collection répondre :

class Panier:
    def __init__(self, articles):
        self.articles = articles

    def __bool__(self):
        return bool(self.articles)


print(bool(Panier([])))           # False
print(bool(Panier(["clavier"])))  # True
PYTHON
Un instant

Créez un compte pour exécuter ce code

Inscrivez-vous gratuitement pour modifier et exécuter du code Python directement dans votre navigateur.

Maintenant, voyons un autre exemple. En effet, on ne va pas se limiter à « est-ce que c'est vide ? ». Prenons un compte client : j'ai envie que True veuille dire « le client peut passer commande ».

class CompteClient:
    def __init__(self, nom, actif, solde):
        self.nom = nom
        self.actif = actif
        self.solde = solde

    def __bool__(self):
        # Le compte est exploitable s'il est actif ET approvisionne
        return self.actif and self.solde > 0


comptes = [
    CompteClient("Patrick", True, 120.0),
    CompteClient("Sebastien", True, 0.0),
    CompteClient("Elodie", False, 80.0),
]

for compte in comptes:
    if compte:
        print(f"{compte.nom} : commande possible")
    else:
        print(f"{compte.nom} : commande bloquee")

# Patrick : commande possible
# Sebastien : commande bloquee
# Elodie : commande bloquee
PYTHON
Un instant

Créez un compte pour exécuter ce code

Inscrivez-vous gratuitement pour modifier et exécuter du code Python directement dans votre navigateur.

À noter

Faites en sorte que la méthode ne produise pas d'effet de bord.

Quelle différence entre __bool__ et __len__ ?

__len__ suffit la plupart du temps quand notre objet est une collection. __bool__ devient surtout intéressant quand on tombe sur un cas comme l'exemple du dessus, c'est-à-dire quand vrai ne veut pas dire non vide. Un autre cas tout aussi intéressant, c'est quand compter est coûteux.

Prenons un autre exemple : un journal d'erreurs dans lequel on veut savoir s'il y a au moins une erreur.

JOURNAL = [
    "INFO connexion de Patrick",
    "ERREUR echec de paiement",
    "INFO connexion de Sebastien",
    "ERREUR timeout base de donnees",
]


class Recherche:
    def __init__(self, lignes, terme):
        self.lignes = lignes
        self.terme = terme

    def _contient(self, ligne):
        print(f"Analyse de : {ligne}")
        return self.terme in ligne

    def __bool__(self):
        # any() s'arrete des la premiere correspondance
        return any(self._contient(ligne) for ligne in self.lignes)


recherche = Recherche(JOURNAL, "ERREUR")

if recherche:
    print("Au moins une erreur dans le journal")

# Analyse de : INFO connexion de Patrick
# Analyse de : ERREUR echec de paiement
# Au moins une erreur dans le journal
PYTHON
Un instant

Créez un compte pour exécuter ce code

Inscrivez-vous gratuitement pour modifier et exécuter du code Python directement dans votre navigateur.

Deux lignes analysées, pas besoin de tout parcourir.

class Panier:
    def __init__(self, articles):
        self.articles = articles

    def __bool__(self):
        return len(self.articles)


bool(Panier(["clavier"]))
# TypeError: __bool__ should return bool, returned int
PYTHON

Un piège un peu vicieux avec and : l'opérateur ne renvoie pas forcément True ou False mais l'une des deux valeurs qu'on lui donne. Prenons un exemple :

class CompteClient:
    def __init__(self, nom, actif, solde):
        self.nom = nom
        self.actif = actif
        self.solde = solde

    def __bool__(self):
        return self.actif and self.solde > 0


# actif vaut 0 (un entier) et non False
bool(CompteClient("Patrick", 0, 120.0))
# TypeError: __bool__ should return bool, returned int
PYTHON

actif vaut 0 au lieu de False, __bool__ renvoie donc un entier, ce qui explique l'erreur. Pour régler le problème, on peut utiliser la fonction bool() : bool(self.actif and self.solde > 0).

Ne pas confondre faux et absent

Un objet peut être évalué comme faux mais peut exister 🤯. Je vous laisse méditer… Ça sera plus simple avec un exemple :

class Panier:
    def __init__(self, articles):
        self.articles = articles

    def __bool__(self):
        return bool(self.articles)


def resumer(panier):
    if not panier:
        return "Aucun panier fourni"
    return f"{len(panier.articles)} article(s) dans le panier"


print(resumer(None))        # Aucun panier fourni
print(resumer(Panier([])))  # Aucun panier fourni <- le panier existe pourtant
PYTHON
Un instant

Créez un compte pour exécuter ce code

Inscrivez-vous gratuitement pour modifier et exécuter du code Python directement dans votre navigateur.

Un panier vide existe bien... même s'il est vide ! L'objet est bien là. Donc pour tester si un objet est bien présent, vérifiez qu'il n'est pas None :

def resumer_corrige(panier):
    if panier is None:
        return "Aucun panier fourni"
    return f"{len(panier.articles)} article(s) dans le panier"


print(resumer_corrige(None))        # Aucun panier fourni
print(resumer_corrige(Panier([])))  # 0 article(s) dans le panier
PYTHON

Bravo, tu es prêt à passer à la suite

Rechercher sur le site

Inscris-toi à Docstring

Pour commencer ton apprentissage.

Tu as déjà un compte ? Connecte-toi.