Définition
Dans une boucle, tous les éléments ne méritent pas le même sort. Un fichier de données contient des lignes vides et des commentaires, une liste d'inscrits contient des adresses invalides. Il faut donc pouvoir dire, au beau milieu du parcours : celui-là, on le laisse de côté, on regarde le suivant.
continue est exactement cette phrase, réduite à un mot. Dès que Python le rencontre, il abandonne le tour en cours : tout ce qui se trouve en dessous dans le bloc n'est simplement pas exécuté pour cet élément.
Ce qu'il n'interrompt pas, en revanche, c'est la boucle. Elle repart au tour suivant comme si rien ne s'était passé, avec l'élément d'après. C'est là toute la différence avec break, qui met fin au parcours pour de bon.
L'exemple le plus fréquent est la lecture d'un fichier ligne à ligne, où deux catégories sont écartées avant que le vrai travail ne commence.
for ligne in fichier:
if not ligne.strip():
continue # ligne vide : rien à traiter
if ligne.startswith("#"):
continue # commentaire : rien à traiter non plus
traiter(ligne)Les deux if font office de portiers. Chacun renvoie les éléments dont il ne veut pas, et traiter ne voit jamais que les lignes ayant passé les deux contrôles. Le corps utile de la boucle tient sur une seule ligne, tout en bas, sans la moindre condition autour.
Pourquoi il aplatit le code
L'objection vient tout de suite : la même chose s'écrit avec des conditions imbriquées, sans mot-clé particulier. C'est vrai, et le résultat est rigoureusement identique.
# Le même filtre, sans continue
for ligne in fichier:
if ligne.strip():
if not ligne.startswith("#"):
traiter(ligne)Avec deux filtres, l'imbrication reste supportable. L'ennui commence quand un troisième puis un quatrième critère arrivent, six mois plus tard : le code utile se retrouve à seize espaces du bord, et la condition qui a permis d'y arriver a disparu du haut de l'écran. Lire une ligne demande alors de remonter et de garder en tête tout ce qui est vrai à cet endroit.
La version avec continue ne réclame pas cet effort. Chaque filtre est une petite phrase autonome, lisible seule, et la seule chose à savoir en arrivant au bas de la boucle est que tous les cas gênants sont déjà partis. Ajouter un critère revient à insérer deux lignes, en retirer un revient à en supprimer deux.
Ce motif porte un nom, la clause de garde, et il ne concerne pas que les boucles : dans une fonction, un return placé très tôt joue exactement le même rôle vis-à-vis du corps qui suit.
Les deux moments où il surprend
Le premier concerne la boucle while, et il vaut mieux en comprendre la raison que retenir la règle. Avec for, c'est la boucle qui avance toute seule d'un élément au suivant : sauter la fin du bloc ne l'empêche donc jamais de progresser. Avec while, la progression est à la charge du code que vous écrivez, en général une incrémentation de compteur. Si cette incrémentation se trouve après le continue, elle n'est jamais atteinte.
# Boucle infinie : i n'augmente jamais quand la condition est vraie
i = 0
while i < 10:
if i % 2 == 0:
continue # on repart au test avec le même i, indéfiniment
i += 1Le programme ne plante pas et n'affiche rien : il tourne, et il faut l'arrêter à la main. Dans une boucle while, faites avancer le compteur avant le continue, ou passez à un for qui vous en dispense.
Le second concerne finally. Un continue placé à l'intérieur d'un try ne s'échappe pas en douce : le bloc finally s'exécute quand même, juste avant le passage au tour suivant. C'est le comportement voulu, puisque finally existe précisément pour libérer ce qui doit l'être quoi qu'il arrive, mais il déroute la première fois qu'un message s'affiche pour une itération qu'on croyait avoir sautée.
Un cas concret
Le motif prend toute sa valeur dès qu'il faut rendre compte des rejets. Ici, chaque ligne d'un fichier est vérifiée sur deux critères, et chaque refus est consigné avec sa raison au moment précis où il est décidé.
lignes_valides = 0
for numero, ligne in enumerate(fichier, start=1):
colonnes = ligne.split(";")
if len(colonnes) != 4:
journal.append(f"Ligne {numero} : colonnes manquantes")
continue
if not colonnes[2].isdigit():
journal.append(f"Ligne {numero} : quantité non numérique")
continue
enregistrer(colonnes)
lignes_valides += 1Regardez la place du message d'erreur : il est collé au test qui l'a déclenché, sur les deux lignes qui précèdent le continue. Se tromper de motif devient difficile, et oublier d'en écrire un aussi : le rejet et sa trace forment un seul geste.
À la fin du traitement, journal contient un rapport ligne par ligne et lignes_valides le compte de ce qui est passé. Écrit avec des conditions imbriquées, le même code aurait ses messages regroupés dans des else, loin des tests correspondants, et le lien entre un refus et son explication deviendrait une affaire de relecture attentive.
Questions fréquentes
Quelle différence entre continue et break ?
continue saute un élément et poursuit le parcours, break arrête tout et sort de la boucle sur-le-champ. Le premier filtre, le second interrompt. Les confondre donne un programme qui semble ne traiter qu'un seul élément au lieu de la collection entière, et c'est un des bugs les plus déroutants au début, parce qu'aucune erreur n'est levée pour le signaler.
continue affecte-t-il la boucle extérieure ?
Non, il n'agit que sur la boucle qui le contient directement, exactement comme break. Dans deux boucles imbriquées, un continue écrit dans la boucle interne fait passer à l'élément suivant de cette boucle interne, et la boucle externe n'en saura jamais rien. Pour sauter un tour de la boucle externe, il faut un indicateur, ou déplacer la boucle interne dans une fonction.
Vaut-il mieux filtrer avant la boucle ?
Quand le critère est simple et unique, souvent oui : une compréhension de liste ou un appel à filter exprime l'intention en une ligne et se lit d'un coup d'œil. continue reprend l'avantage dès que les critères se multiplient, qu'ils méritent chacun un commentaire, ou qu'un rejet doit être compté ou consigné quelque part. Le filtre en amont dit ce qu'on garde, continue dit pourquoi on écarte.