Le mot-clé else en Python ne signifie pas toujours « sinon ». Placé après une boucle for ou un bloc try, il change complètement de rôle. Cette ambiguïté provoque des bugs silencieux que ni le message d’erreur ni le traceback ne signalent, parce que le code est syntaxiquement correct.
Le piège du else « post-succès » dans for et try en Python
Vous avez déjà vu un else après un if : il s’exécute quand la condition est fausse. Le réflexe naturel consiste à transposer cette logique partout. Avec une boucle for, ce réflexe mène droit au bug.
Un else rattaché à un for ne signifie pas « si la boucle n’a rien trouvé ». Il signifie : le bloc else s’exécute uniquement si aucun break n’a interrompu la boucle. La nuance est technique, mais ses conséquences sont concrètes.
Prenons un exemple simple. Ce script cherche un nombre négatif dans une liste :
for n in [3, 7, -1, 4]:
if n < 0:
print("Trouvé :", n)
break
else:
print("Aucun négatif")
Ici, le break est atteint, donc le bloc else ne s’exécute pas. Retirez le -1 de la liste : la boucle se termine normalement, et le else affiche « Aucun négatif ». Jusque-là, tout semble logique.
Le problème arrive quand un développeur oublie le break ou le supprime lors d’un refactoring. Sans break, le bloc else s’exécute systématiquement après la boucle, quel que soit le contenu de la liste. Le code ne plante pas. Aucun traceback. Le script produit un résultat faux sans aucun signal d’alerte.

Else après try except : isoler la ligne risquée du post-traitement
Le même mot-clé else joue un rôle différent dans un bloc try/except. Le code placé dans le else ne s’exécute que si le try n’a levé aucune exception. C’est un vrai « post-succès ».
Pourquoi ne pas mettre ce code directement à la fin du try ? Parce que le else d’un try n’est pas protégé par les except du même bloc. Si une erreur survient dans le else, elle remonte sans être capturée. C’est exactement le comportement souhaité : vous distinguez l’erreur de la ligne risquée d’une erreur dans le traitement qui suit.
Voici la structure recommandée :
try:
resultat = int(valeur) # seule ligne risquée
except ValueError:
print("Conversion impossible")
else:
print("Résultat :", resultat * 2) # post-traitement
finally:
print("Bloc terminé")
Si int(valeur) échoue, le except gère le ValueError. Si la conversion réussit, le else exécute le calcul. Si ce calcul produit lui-même une erreur (par exemple un TypeError inattendu), elle n’est pas avalée par le except ValueError. Elle remonte dans la pile d’appels et reste visible lors du débogage.
Le cas du except nu qui masque tout
Un except sans type d’exception capture tout, y compris les fautes de frappe sur les noms de variables (NameError) et les interruptions clavier (KeyboardInterrupt). Ce pattern rend le débogage quasi impossible dans un script de production.
- Un
except Exceptioncapture les erreurs applicatives sans bloquer les signaux système, ce qui constitue un premier garde-fou raisonnable. - Un
exceptciblé (par exempleexcept ValueError, KeyError) documente les erreurs attendues et laisse remonter les autres. - Isoler la ligne risquée dans le try et déplacer le reste dans else empêche qu’une erreur de post-traitement soit confondue avec l’erreur initiale.
Corrections pas à pas des erreurs fréquentes avec for else
Les bugs liés au for/else partagent un schéma commun : le développeur écrit un else en pensant « sinon » alors que Python lit « pas de break ».
Erreur 1 : else sans break dans la boucle
C’est le cas le plus courant. Le code ressemble à ceci :
for user in users:
if user.active:
print(user.name)
else:
print("Aucun utilisateur actif")
Sans break après le print, le else s’exécute dans tous les cas. Le message « Aucun utilisateur actif » apparaît même quand la liste contient des utilisateurs actifs. La correction consiste à ajouter un break après la ligne print(user.name), ou à remplacer le pattern par une variable booléenne.
Erreur 2 : le else indenté au niveau du if
L’indentation détermine si le else appartient au if ou au for. Un décalage d’un niveau transforme un « post-boucle » en « branche conditionnelle ». Vérifiez toujours à quel bloc votre else est rattaché en contrôlant l’indentation.

Erreur 3 : supprimer le break lors d’un refactoring
Quand on simplifie une boucle, le break disparaît parfois. Le else reste en place et devient du code mort qui s’exécute à chaque itération complète. Les linters modernes signalent désormais un else de boucle inutile quand la boucle ne contient pas de break, avec une recommandation de supprimer le else et de dédenter le code.
Quand utiliser (et éviter) le else de boucle en Python
Le for/else a un cas d’usage légitime : les recherches avec sortie anticipée. Vous parcourez une collection, vous cherchez un élément, et le else gère le cas « non trouvé ». En dehors de ce schéma précis, le pattern crée plus de confusion qu’il n’en résout.
- Utilisez
for/elsequand la boucle contient unbreakconditionnel et que vous avez besoin d’un traitement « non trouvé » juste après. - Préférez une variable booléenne (
found = False) si la logique de recherche est complexe ou imbriquée, car leelsede boucle devient illisible avec plusieurs niveaux. - Dans un bloc
try/except, placez systématiquement le post-traitement dans leelseplutôt qu’à la fin dutry, pour séparer la gestion d’erreur du traitement nominal. - Supprimez tout
elsede boucle orphelin (sansbreakcorrespondant) : c’est du code trompeur.
La règle de lisibilité la plus fiable reste celle-ci : si un collègue doit relire votre else deux fois pour comprendre à quel bloc il appartient, remplacez-le par une structure plus explicite. Un code clair bat toujours un raccourci syntaxique ambigu.

