Le bug Python subtil que j'ai créé en comprenant mal `bool()`

Le bug Python subtil que j’ai créé en comprenant mal bool()

J’ai introduit un bug subtil dans un programme Python à cause d’une hypothèse simple. Une variable nommée is_enabled devait contenir une chaîne représentant un booléen : soit "true", soit "false".

Pour évaluer cette variable, j’ai écrit cette condition :

1if bool(is_enabled.capitalize()):
2    # Faire quelque chose si True

Au premier coup d’œil, cela semble logique. Si is_enabled vaut "false", alors is_enabled.capitalize() devient "False". La fonction bool("False") devrait donc renvoyer False, n’est-ce pas ?

C’est faux. La condition a renvoyé True à chaque fois, bloquant la logique de mon programme.

Le piège de l’intuition symétrique

J’ai fait cette erreur principalement à cause d’une fausse impression de symétrie dans la conversion de types en Python. Le langage permet de passer facilement d’un type à un autre. La plupart des conversions semblent bidirectionnelles :

  • Avec les entiers

    • str(1) donne "1"
    • int("1") donne 1
  • Avec les flottants

    • str(1.5) donne "1.5"
    • float("1.5") donne 1.5
  • Avec les booléens

    • str(True) donne "True" et str(False) donne "False"

Le cerveau construit naturellement un modèle où passer la représentation textuelle d’une valeur à son constructeur de type inverse le processus. On s’attend à ce que bool("True") donne True et que bool("False") donne False.

Pourtant, cette symétrie s’effondre lors de la conversion d’une chaîne en booléen.

Pourquoi Python évalue "False" comme True

En Python, le constructeur bool() n’analyse pas le contenu textuel d’une chaîne (contrairement à int() ou float()). Il évalue la chaîne selon les règles fondamentales des tests de valeur de vérité (ou “truthiness”).

D’après la Documentation Python officielle, un objet est considéré comme vrai, sauf si sa classe définit une méthode __bool__() qui renvoie False ou une méthode __len__() qui renvoie 0.

Pour les chaînes de caractères, cela implique que :

  • La chaîne vide "" est False (car sa longueur est de 0).
  • Toute chaîne non vide est True (peu importe les caractères qu’elle contient).

À cause de cette règle, toutes ces instructions évaluent à True :

1print(bool("true"))   # True
2print(bool("False"))  # True
3print(bool("0"))      # True
4print(bool(" "))      # True (contient un espace)

Mon code bool(is_enabled.capitalize()) prenait la valeur "false" et la transformait en "False". Comme "False" est une chaîne non vide, Python l’a évaluée comme True, en accord strict avec ses règles.

Comment analyser correctement les chaînes booléennes

Si vous devez convertir une variable chaîne contenant "true" ou "false" en un véritable booléen, vous ne pouvez pas utiliser bool(). Utilisez plutôt l’une de ces approches explicites :

1. Comparaison directe de chaînes (Recommandé)

La méthode la plus propre et la plus lisible consiste à comparer directement la chaîne convertie en minuscules avec "true".

1# Évalue à True uniquement si is_enabled est une forme textuelle de "true"
2is_valid = is_enabled.lower() == "true" 
2. Utilisation d’un dictionnaire de correspondance

Un dictionnaire est idéal pour gérer explicitement les deux états valides et intercepter les valeurs inattendues.

1STR_TO_BOOL = {"true": True, "false": False}
2
3# Renvoie True, False, ou None si la chaîne ne correspond à aucun des deux
4is_valid = STR_TO_BOOL.get(is_enabled.lower()) 

Tip

Leçons apprises

  • N’utilisez jamais bool(chaine) pour analyser des valeurs booléennes textuelles comme "False".
  • Rappelez-vous que toute chaîne non vide est truthy en Python.
  • Python privilégie la longueur explicite de la collection plutôt que le contenu textuel lors de la conversion en booléen.

En espérant que cela vous évitera de tomber dans le même piège de truthiness lors de vos prochaines conversions de chaînes en booléens avec Python !

Articles Connexes

Explication Illustrative de la Faute, de l'Erreur, de la Défaillance, du Bug et du Défaut Logiciel

Explication Illustrative de la Faute, de l’Erreur, de la Défaillance, du Bug et du Défaut Logiciel

Les logiciels ne se comportent pas toujours comme prévu. Des erreurs dans l’implémentation ou dans la spécification des exigences peuvent …

Lire la suite
Configuration Go YAML : corrigez le bug de configuration silencieux avec `mapstructure`

Configuration Go YAML : corrigez le bug de configuration silencieux avec mapstructure

Évitez les bugs silencieux de configuration Go dans vos microservices. Découvrez pourquoi le chargement direct d’une struct YAML, JSON ou TOML …

Lire la suite
Comment vérifier l'accessibilité d'un port TCP en Python (Synchrone & Asynchrone)

Comment vérifier l’accessibilité d’un port TCP en Python (Synchrone & Asynchrone)

Rien ne bloque un déploiement plus vite qu’une erreur « Connection Refused » inattendue. Que vous enquêtiez sur un changement de firewall …

Lire la suite