Boucles Python : tirer parti de for Python else pour écrire moins de code

La clause for Python else reste l’une des syntaxes les plus méconnues du langage. Elle permet de supprimer des variables drapeaux, de réduire l’indentation et de rendre le flux de contrôle plus lisible, à condition de comprendre exactement quand le bloc else s’exécute. Cet article mesure le gain concret en lignes de code et en lisibilité entre un pattern classique avec drapeau booléen et son équivalent avec for…else.

Comparatif : pattern drapeau booléen contre for…else en Python

La majorité des développeurs qui découvrent Python reproduisent un schéma hérité de C ou Java : déclarer une variable found = False, la passer à True dans la boucle, puis tester sa valeur après la boucle. Le for…else supprime cette mécanique.

Critère Pattern drapeau (found = False) Pattern for…else
Variables intermédiaires 1 booléen minimum Aucune
Nombre de lignes (recherche simple) 6-8 lignes 4-5 lignes
Niveaux d’indentation 2 (boucle + if post-boucle) 1 (le else est au niveau du for)
Risque de bug lié à l’oubli du drapeau Fréquent lors de refactoring Inexistant
Compatibilité linters (Pylint) Aucun avertissement Avertissement si pas de break dans la boucle

La différence peut sembler modeste sur un exemple isolé. Elle s’accumule dans un projet où des dizaines de recherches parcourent des collections.

Jeune programmeuse Python travaillant sur des boucles for-else depuis son bureau à domicile avec un laptop ouvert sur un code Python

Mécanique du else après une boucle for en Python

Le bloc else d’une boucle for s’exécute uniquement quand la boucle se termine sans rencontrer de break. C’est la règle unique, et c’est elle qui génère la confusion : beaucoup de développeurs lisent « else » comme « sinon la boucle n’a pas tourné », alors que la sémantique réelle est « aucun break n’a interrompu l’itération ».

Un exemple concret : chercher un élément dans une liste de valeurs. Avec le pattern classique, on écrit une variable found, on la modifie dans le if, on sort avec break, puis on teste found après la boucle. Avec for…else, le bloc else contient directement le traitement du cas « non trouvé ».

Condition obligatoire : un break dans le corps de la boucle

Pylint signale désormais comme code inutile une clause else sur une boucle qui ne contient aucun break. La raison est logique : sans break, le bloc else s’exécute systématiquement, ce qui le rend équivalent à du code placé après la boucle, sans aucune valeur de contrôle de flux.

Cette règle du linter formalise une bonne pratique. Avant d’ajouter un else à une boucle for, vérifiez que le corps contient bien un break conditionnel. Sans ce break, le else n’apporte rien et nuit à la lisibilité.

Cas d’usage concrets du for…else pour réduire le code Python

Trois situations reviennent régulièrement dans du code Python où le for…else remplace avantageusement un drapeau booléen.

  • Recherche d’un élément dans une collection : parcourir une liste, un dictionnaire ou un ensemble pour trouver une valeur qui satisfait une condition, et déclencher un traitement spécifique si aucun élément ne correspond.
  • Validation de contraintes sur tous les éléments : vérifier que chaque élément d’une séquence respecte une règle (format, type, plage de valeurs). Le break intervient dès qu’un élément invalide est détecté, et le else confirme que tout est conforme.
  • Recherche avec effet de bord : parcourir des fichiers, des connexions réseau ou des entrées utilisateur pour trouver la première ressource disponible, avec un else qui gère le cas « aucune ressource trouvée » sans variable intermédiaire.

Dans chacun de ces cas, le gain ne se mesure pas seulement en lignes supprimées. L’absence de drapeau booléen élimine une source de bugs lors du refactoring, car il n’y a plus de variable à initialiser, à mettre à jour et à tester en trois endroits distincts du code.

Piège fréquent : confondre else du for et else du if

Le else d’un if s’exécute quand la condition est fausse. Le else d’un for s’exécute quand la boucle n’a pas été interrompue. Mélanger ces deux sémantiques produit des bugs silencieux, car le code fonctionne sans erreur mais pas comme prévu.

Pour lever l’ambiguïté, certains développeurs lisent mentalement le else comme « no break ». Cette lecture colle exactement au comportement réel de l’interpréteur Python.

Deux ingénieurs logiciels discutant d'une boucle for-else Python devant un écran mural dans un bureau de startup technologique moderne

Limites du for…else et lisibilité en équipe

Le for…else n’est pas toujours le bon choix. Dans une équipe où la majorité des développeurs viennent de Java ou JavaScript, cette syntaxe peut ralentir la revue de code. La lisibilité dépend du contexte humain autant que du contexte technique.

Un autre cas où le for…else atteint ses limites : les boucles imbriquées. Quand deux boucles for sont imbriquées et que chacune porte un else, le flux de contrôle devient difficile à suivre. Extraire la boucle interne dans une fonction séparée résout le problème et conserve la clarté du for…else à un seul niveau.

La recommandation documentée dans la communauté Python privilégie le for…else comme idiome plus pythonique que les drapeaux booléens hérités d’autres langages. En revanche, si la logique de recherche dépasse quelques lignes, une fonction dédiée avec un return explicite reste plus lisible qu’un for…else chargé.

Le critère de décision tient en une question : le break et le else rendent-ils le flux de contrôle plus clair que l’alternative ? Si la réponse est oui, le for…else réduit le code sans sacrifier la maintenance. Si la boucle devient complexe, une fonction avec return produit le même résultat avec moins d’ambiguïté.