Définition
Un programme qui traite mille commandes ne contient pas mille lignes d'expédition. Recopier la même instruction une fois par donnée serait impossible à maintenir, et même à écrire : quand vous tapez le code, vous ignorez combien de commandes arriveront. C'est l'ennui que la boucle résout.
Une boucle répète un bloc de code sans qu'on ait à le réécrire. Vous décrivez le traitement une fois, Python le rejoue autant de fois que nécessaire. C'est ce qui permet à un programme de quinze lignes de traiter un fichier de dix mille lignes.
Python en propose deux formes, et le choix ne relève pas du goût : il dépend de ce que vous savez au moment d'entrer dans la boucle.
# Cas 1 : on connaît la collection, donc le nombre de tours
for commande in commandes:
expedier(commande)
# Cas 2 : on ignore le nombre de tours,
# on sait seulement à quelle condition on s'arrête
while not connexion.est_etablie:
tenter_reconnexion()Choisir entre les deux
La question est toujours la même : est-ce que je sais déjà sur quoi je vais tourner ? Le tableau ci-dessous oppose les deux formes sur les quatre points qui tranchent.
for | while | |
|---|---|---|
| Ce qu'il faut fournir | Une collection à parcourir | Une condition d'arrêt |
| Nombre de tours | Connu à l'avance | Inconnu |
| Risque principal | Aucun, il s'arrête seul | La boucle infinie |
| Usage typique | Une liste, un fichier, un dictionnaire | Une attente, une saisie, un état |
Dès qu'une collection est en jeu, c'est for : elle connaît sa propre longueur, la boucle s'arrête donc seule. Le while se réserve aux cas où la fin dépend de quelque chose qu'on ne peut évaluer qu'après avoir agi, comme une réponse de l'utilisateur ou un serveur qui ne répond pas encore : avant d'avoir agi, vous ignorez si c'était le dernier tour.
Reste le cas auquel on pense aussitôt : aucune collection, juste un nombre de répétitions connu. Il revient malgré tout à for, à qui il faut alors fournir de quoi tourner : c'est le rôle de range.
Le for de Python n'est pas le compteur des autres langages. Il ne compte rien : il demande à un itérable de lui livrer ses éléments un par un. D'où l'absence d'indice à gérer, et l'existence de range quand vous voulez vraiment des nombres.
Piloter le parcours
Une boucle qui va toujours jusqu'au bout ne suffit pas longtemps : dès qu'on cherche un élément précis, continuer après l'avoir trouvé n'a plus de sens. Trois instructions reprennent la main.
break quitte la boucle immédiatement, sans finir le tour en cours. C'est ce qui transforme un parcours complet en recherche : sur dix mille éléments, s'arrêter au bon moment en lit la moitié en moyenne.
continue abandonne le tour en cours et passe au suivant. Il écarte d'emblée les cas qui ne vous intéressent pas, sans enfermer tout le corps de la boucle dans un if : un niveau d'indentation de gagné, et la logique principale reste lisible.
Le else de boucle, propre à Python, déroute au premier contact. Il ne veut pas dire « sinon » : il s'exécute quand la boucle est allée jusqu'au bout sans jamais rencontrer de break. C'est le bloc « je n'ai rien trouvé ».
S'y ajoutent des fonctions qui enrichissent le parcours. enumerate fournit le rang en même temps que l'élément, ce qui supprime le compteur incrémenté à la main. zip parcourt deux collections en parallèle, le cas où l'on est tenté à tort de repasser par des indices.
Ne retirez jamais d'éléments d'une liste pendant que vous la parcourez. Chaque suppression décale les suivants, la boucle en saute donc un sur deux, et rien ne signale l'anomalie : le programme se termine normalement, avec un résultat faux. Parcourez plutôt une copie de la liste.
Quand s'en passer
Toutes les répétitions ne méritent pas une boucle écrite à la main. Le cas le plus fréquent est aussi le plus mécanique : partir d'une collection et en construire une autre, élément par élément. Python offre une écriture dédiée, la compréhension de liste.
# Une boucle qui ne fait qu'accumuler :
# trois lignes pour dire « multiplier chaque prix »
prix_ttc = []
for prix in prix_ht:
prix_ttc.append(prix * 1.2)
# La même chose, en une ligne
prix_ttc = [prix * 1.2 for prix in prix_ht]Ce que la seconde version gagne n'est pas la brièveté, c'est l'intention : elle annonce dès le premier caractère qu'on fabrique une liste, là où la première oblige à lire trois lignes.
Le critère est simple : si le corps de la boucle se réduit à un append, la compréhension l'emporte. Dès qu'il y a plusieurs opérations, des cas à écarter avec explication ou une exception à rattraper, la boucle explicite redevient la bonne réponse.
Questions fréquentes
Comment sortir d'une boucle infinie ?
Pendant l'exécution, l'interruption clavier arrête le programme, Contrôle et C sur la plupart des systèmes. Dans le code, la cause est presque toujours une variable de contrôle jamais modifiée dans le corps, ou modifiée dans une branche qu'un continue placé plus haut empêche d'atteindre.
Peut-on imbriquer des boucles ?
Oui, et c'est courant pour parcourir une grille ou croiser deux collections. Attention au coût : deux boucles imbriquées sur mille éléments chacune font un million de tours. Quand la boucle interne ne sert qu'à retrouver l'élément correspondant, un dictionnaire construit avant le parcours remplace ce passage par une lecture immédiate.
Comment répéter une action un nombre fixe de fois ?
Avec for et range, par exemple for _ in range(5):. Le tiret bas est la convention pour dire que la valeur du compteur ne sert pas, seul le nombre de répétitions compte. Savoir choisir entre les deux boucles et reconnaître quand une compréhension fait mieux que les deux se travaille sur des cas réels dans notre formation Python.