Python : cette étrange structure for else
Par Dad 3.0, 15/04/2020
Niveau : Débutant
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 comprendre pourquoi elles existent, 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.
Ce dont vous aurez besoin…
Les bases de programmation Python en particulier les structures de contrôle (boucles et conditionnelles).
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
forqui permet de répéter une action un certain nombre de fois - La boucle
whilequi 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.
Voyons ces deux codes :
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 qui est classique (et le bon code pour ce cas dans d'autres langages) :
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 faut 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 l'interruption de la boucle (car il a été trouvé) ou celui créé dans la conditionnelle (car aucun élément d'intérêt n’a é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.
