Python : cette étrange structure for else
Je répète souvent lors de mes formations qu’apprendre un langage ne consiste pas seulement à apprendre une sémantique. Un des objectifs de Python est de simplifier la vie des développeurs et pour cela, le langage a des structures qui paraissent bizarres. Souvent, on découvre ces composants sans connaitre la raison de leur création, et ainsi, on passe à coté de leur bénéfice.
Parmi ces structures, il y a le for … else. Tous les développeurs connaissent les instructions de boucles (for et while) et les instructions d’interruption (break et continue). Mais le mot clef else n’est vraiment pas à sa place. Pourtant, il est si évident si on exprime correctement son intention.
Rappel sur les boucles et leur interruption
Une boucle est une structure de base en programmation. Elle permet de répéter un ensemble d’instructions. En Python, il existe deux types de boucles :
- La boucle for qui permet de répéter une action un certain nombre de fois
- La boucle while qui permet de répéter une action tant qu’une condition est vrai.
Python simplifie déjà l’usage de la boucle for. En effet, celle-ci va permettre d’itérer sur une collection, c’est à dire de traiter chaque élément d’une collection.
for knight in ['Arthur', 'Lancelot', 'Robin', 'Bedivere']: print(knight, "is a mighty knight from Camelot")
Cette boucle s’exécutera donc 4 fois. Mais il existe deux mots clefs associés aux boucles qui ont des effets d’interruption :
- continue interrompt l’itération et passe à l’itération suivante. Elle est destinée à ne pas exécuter toutes les instructions de la boucle pour un élément.
- break interrompt l’itération et sort de la structure, en d’autres mots, break interrompt la boucle, les éléments restants ne seront pas traités.
Découverte du mot clef else
Python propose un autre mot clef à utiliser dans les boucles : else. Ce mot clef est connu dans les structures conditionnelles (if … else), mais ici, nous parlons d’un for … else. Techniquement, il est possible d’écrire un while … else, mais vous allez voir que son usage est principalement attendu avec for.
Le mot clef else définit un bloc après la boucle, bloc qui sera exécuté si on est sorti de la boucle autrement que par un break. En d’autres termes, dans le cas d’une boucle for, c’est un bloc qui ne sera exécuté que si la structure parcourt tous les éléments.
Exécutez les deux codes suivants :
for knight in ['Arthur', 'Lancelot', 'Robin', 'Bedivere']: if knight == "Merlin": break print(knight, "is a mighty knight from Camelot") else: print("There is no wizard in this team")
Vous verrez que tous les éléments sont parcourus et le bloc else est exécuté, alors que pour le suivant :
for knight in ['Arthur', 'Lancelot', 'Robin', 'Bedivere']: if knight == "Lancelot": break print(knight, "is a mighty knight from Camelot") else: print("No one is gone with the Queen")
La boucle s’interrompt au second élément et le code du bloc else n’est pas exécuté.
Maintenant, essayons de bien formuler l’usage.
Trouver un élément sinon…
C’est bien le sens de ce mot clef. En effet, souvent, nous recherchons un élément dans une liste, si nous le trouvons, nous faisons quelque chose avec sinon, nous devons faire autre chose. On devrait d’ailleurs dire que la structure est for…break…else. Pour chaque élément, si nous trouvons break, sinon.
Dans la pratique, ça vous évite ce genre de code :
found = False for knight in ['Arthur', 'Lancelot', 'Robin', 'Bedivere']: if knight == "Lancelot": found = True break print(knight, "is a mighty knight from Camelot") if not found: print("No one is gone with the Queen")
La structure for…else évite donc de faire appel à un flag, une variable drapeau qui est un indicateur si l’élément est trouvé. C’est un intérêt car la structure précédente n’est en fait pas une mais deux structures indépendantes : la boucle et le test du flag. Vous avez donc un risque que du code vienne s’insérer entre les deux avec comme conséquence de compliquer la compréhension de l’intention de ce code.
Mais l’intention du code, c’est ç dire identifier rapidement ce que fait le code, c’est réellement la raison d’une telle structure. Si vous regardez le code avec le flag (attention, regarder, pas le lire en détail), vous voyez une boucle et une conditionnel. Il fait le lire en détail pour identifier la dépendance et que la conditionnelle est exécutée en absence de valeur trouvée. De ce fait, vous ne pouvez pas non plus identifier l’objectif de l’itération qui est la recherche d’un élément.
À l’opposée, avec la structure for…else, on identifie immédiatement que l’itération est une recherche dans la collection et qu’une action sera réalisée si l’élément n’est pas trouvé. En Python, il est important d’identifier ces structures qui se veulent avoir un sens.
Pour conclure, je vous rappelle qu’une variable créée dans une boucle ou une conditionnelle est visible en dehors. Vous pouvez donc écrire du code de ce type :
for element in collection: if is_what_I_need(element): break else: element = create_new_element() collection.append(element) do_something_with(element)
Ici, element contiendra le dernier élément affecté à la variable avant la sortie de la boucle ou celui créé dans la conditionnelle (si l’élément d’intérêt n’a pas été trouvé).
N’hésitez donc pas, pour gagner en lisibilité, à utiliser cette structure for…break…else lorsque vous recherchez un élément dans une collection ou sinon vous devez faire quelque chose.
Si vous avez aimé ce post, n’hésitez pas à laisser un commentaire ci-dessous ou sur la page Facebook 😉
À propos de... Darko Stankovski
iT guy, photographe et papa 3.0, je vous fais partager mon expérience et découvertes dans ces domaines. Vous pouvez me suivre sur les liens ci-dessous.