Avec la boucle while Python, un bloc de code est exécuté de manière répétée tant qu’une condition est vraie. Les boucles while sont utilisées en premier lieu dans Python lorsque le nombre d’ité­ra­tions né­ces­saires n’est pas déterminé à l’avance. Nous ex­pli­quons comment fonc­tionne la boucle while en Python.

Conseil
Apprenez à écrire du code Python de manière autonome grâce à notre tutoriel Python.

Qu’est-ce que la boucle while en Python ?

La boucle while en Python est une structure de contrôle. Les struc­tures de contrôle dé­ter­mi­nent le chemin de code à suivre lors de l’exécution. Les boucles sont gé­né­ra­le­ment utilisées pour exécuter un bloc de code de manière répétée. Voici un aperçu des prin­ci­pales struc­tures de contrôle en Python :

Structure de contrôle en Python Ex­pli­ca­tion
Bran­che­ment if-else Exécute le bloc de code une fois, dans la mesure où la condition est vraie
Boucle while en Python Exécute le bloc de code de manière répétée tant que la condition est vraie
Boucle for en Python Itère sur les éléments d’une col­lec­tion en exécutant un bloc de code pour chaque élément

Gé­né­ra­le­ment, les débutants en pro­gram­ma­tion qui ne con­nais­sent pas les boucles tentent de re­pro­duire leur fonc­tion­na­lité. Au lieu de définir une structure de données avec plusieurs éléments et de la traiter avec une boucle, ils dé­fi­nis­sent une variable distincte pour chaque ensemble. De plus, le code pour le trai­te­ment des ensembles est dupliqué. Il en résulte un code sous-optimal, illustré ici par l’exemple de l’affichage de trois personnes avec leur nom et leur âge :

person1 = ('Jim', 17)
person2 = ('Jack', 42)
person3 = ('John', 63)
print(f"{person1[0]}, {person1[1]} years old")
print(f"{person2[0]}, {person2[1]} years old")
print(f"{person3[0]}, {person3[1]} years old")

En plus de la du­pli­ca­tion in­dé­si­rable du code, cette approche pose un problème lo­gis­tique : si le nombre d’ensembles n’est connu qu’au moment de l’exécution, les variables in­di­vi­duelles cor­res­pon­dantes ne peuvent pas être définies à l’avance. Pour résoudre ce problème, on utilise des col­lec­tions d’éléments et des boucles pour les traiter.

Si l’on sait au moment de l’exécution combien de ré­pé­ti­tions sont né­ces­saires, la boucle for en Python donne le meilleur résultat. A titre d’exemple, nous re­pro­dui­sons le code de sortie de trois personnes avec leur âge à l’aide de la boucle for. Le code fonc­tionne sans du­pli­ca­tion et ne contient que deux variables, quel que soit le nombre d’en­re­gis­tre­ments :

people = ('Jim', 'Jack', 'John')
ages = (17, 42, 63)
for person, age in zip(people, ages):
    print(f"{person}, {age} years old")

Con­trai­re­ment à la boucle for, une boucle while en Python est utilisée lorsque l’on ne sait pas combien de ré­pé­ti­tions seront né­ces­saires. Un exemple est l’échange de messages via une connexion ouverte. Tant que la connexion reste ainsi, les messages sont traités. Une ins­truc­tion if dans le corps de la boucle évalue un signal et met fin à la connexion si on le souhaite :

while connection_open():
    print('Ready to receive')
    process_messages()
    if should_close_connection():
        close_connection()
# once loop terminates
print('Connection was closed')

Les boucles while sont donc utilisées pour des ré­pé­ti­tions en principe il­li­mi­tées. Des exemples connus sont les dis­tri­bu­teurs au­to­ma­tiques de billets, l’invite de commande Linux et la « Read-Eval-Print Loop » (REPL) de Python. Voici une il­lus­tra­tion sché­ma­tique d’une exécution de REPL par une boucle sans fin while :

# Loop
while True:
    # Read user input
    user_input = read_input()
    # Evaluate input and produce result
    result = eval(user_input)
    # Print result
    print(result)

Comment fonc­tionne la boucle while en Python ?

La boucle while de Python fonc­tionne de manière similaire à l’ins­truc­tion de bran­che­ment if-else de Python. Les deux struc­tures de contrôle se composent de deux parties :

  1. Une condition qui est évaluée
  2. Un corps contenant des ins­truc­tions

La dif­fé­rence réside dans le nombre de fois où le corps est exécuté. Le corps d’une ins­truc­tion if est exécuté au maximum une fois :

if condition:
    run_once()

Con­trai­re­ment à l’ins­truc­tion if, le corps de la boucle while en Python peut être exécuté plusieurs fois :

while condition:
    run_again()

Lors du passage d’une boucle while Python sur un modèle classique :

  1. La condition est évaluée
  2. Si la condition est vraie, le corps de la boucle est exécuté
  3. La condition est à nouveau évaluée :
    1. Si la condition est toujours vraie, le processus se répète
    2. Si la condition n’est pas vraie, la boucle se termine

Comme l’ins­truc­tion if, une boucle while en Python peut disposer d’un bloc else optionnel. Le bloc else est exécuté une seule fois si la condition est ou devient fausse :

while False:
    # this code doesn't loop
    never_runs()
else:
    # instead, this code runs once
    runs_once()

Quelle est la dif­fé­rence entre la boucle for et la boucle while dans Python ?

La boucle while dans Python est également ap­pa­ren­tée à la boucle for. Les deux exécutent un bloc de code de manière répétée. On parle également d’« ité­ra­tions ». La dif­fé­rence réside dans le nombre de ré­pé­ti­tions.

En Python, les boucles for sont utilisées en premier lieu pour itérer sur les éléments d’une col­lec­tion. Le nombre maximal d’ité­ra­tions est limité par la longueur de la col­lec­tion. Nous allons itérer sur la lettre du mot « Python » et éditer chaque lettre sé­pa­ré­ment :

for letter in 'Python':
    print(letter)

La boucle while de Python n’est pas spé­cia­li­sée dans l’itération sur des col­lec­tions et peut être utilisée de manière flexible. En général, la boucle while est la cons­truc­tion de boucle de base. La fonc­tion­na­lité d’une boucle for peut être cons­truite à l’aide d’une boucle while ; l’inverse n’est pas vrai.

Regardons quelques exemples. Nous re­pro­dui­sons la fonc­tion­na­lité d’une boucle for tra­di­tion­nelle avec une variable de boucle numérique en tant que boucle while Python. Pour cela, nous dé­fi­nis­sons une variable compteur à l’extérieur de la boucle et nous in­cré­men­tons sa valeur à l’intérieur du corps de la boucle :

counter = 0
limit = 10
while counter < limit:
    print(counter)
    counter += 1

La boucle for équi­va­lente est plus courte et plus directe :

for counter in range(10):
    print(counter)

Il en va de même lorsque nous utilisons une boucle while pour itérer sur la lettre d’un mot. Nous utilisons un itérateur et la fonction next(). Lorsque l’itérateur est épuisé, « None » est renvoyé à la place d’une lettre ; la boucle se termine. Le code qui en résulte est nettement plus compliqué que la boucle for équi­va­lente. Il est évident que la boucle while de Python n’est pas l’outil optimal pour résoudre ce problème :

word = 'Python'
letters = iter(word)
letter = ''
while letter is not None:
    letter = next(letters, None)
    if letter:
        print(letter)

Attention aux boucles infinies while dans Python

Les boucles while en Python sont notamment in­té­res­santes pour im­plé­men­ter des boucles sans fin. Cela peut sembler paradoxal au premier abord. En effet, les boucles infinies produites par erreur sont re­dou­tables. La condition ne devient jamais fausse et le programme reste bloqué :

while True:
    print("Forever…")

Il existe de nombreux cas d'ap­pli­ca­tion pour les boucles infinies in­ten­tion­nelles. Une boucle infinie while produite par inad­ver­tance est gé­né­ra­le­ment causée par une ex­pres­sion qui est toujours évaluée à True. En voici un exemple :

while 1 == 1 + 1 - 1:
    print("And ever…")
Conseil
Si vous êtes pris dans une boucle sans fin while dans le REPL Python, la com­bi­nai­son de touches Ctrl + C vous aidera. Elle envoie un signal d’arrêt à l’in­ter­pré­teur Python, qui in­ter­rompt l’exécution de la boucle.

Annuler et ignorer les passages d’une boucle while en Python

En règle générale, une boucle while itère jusqu’à ce que la condition de la boucle ne soit plus vraie. Une astuce courante consiste à utiliser une variable « flag » comme condition. Pour cela, une variable booléenne est définie en dehors de la boucle et évaluée dans la condition de boucle. Lorsqu’une certaine condition est atteinte à l’intérieur du corps de la boucle, nous activons alors le flag. Lors de l’éva­lua­tion de la condition avant le prochain passage, la nouvelle valeur entraîne la fin de la boucle :

aborted = False
while not aborted:
    print("Still going…")
    if some_cond:
        # will prevent next iteration
        aborted = True

Ce modèle est fréquent, mais il n’est pas très élégant. Que se passe-t-il si, dans le corps de la boucle, du code sup­plé­men­taire est ajouté après la variable flag ? Celui-ci devra alors être ignoré. Pour cela, Python connaît l’ins­truc­tion break dans les boucles while.

Si une ins­truc­tion break est exécutée dans une boucle, la boucle se termine im­mé­dia­te­ment. L’ins­truc­tion break dans les boucles est donc similaire à l’ins­truc­tion return dans les fonctions. Cependant, break ne renvoie pas de valeur. Il est courant d’utiliser l’ins­truc­tion break pour in­ter­rompre une boucle sans fin :

while True:
    print("Still going…")
    if some_cond:
        break
        # we never get here
        print("We shouldn't be here")
# we end up here after breaking
print("Done.")

L’ins­truc­tion continue est proche de l’ins­truc­tion break. Si une ins­truc­tion continue est exécutée dans le corps de la boucle, le code suivant est ignoré. L’itération suivante se poursuit. Avec break et continue, il est possible d’im­plé­men­ter des menus simples, basés sur du texte, comme on les connaît des premiers jeux in­for­ma­tiques :

# `continue` takes us here
while True:
    print("Press G to start a new game")
    print("Press S to see stats")
    print("Press M for main menu")
    print("Press Q to quit")
    
    key_press = input("Your choice \n")[0].upper()
    print(f"You pressed {key_press}")
    
    if key_press == "G":
        # start game routines
        print("Starting game …")
    elif key_press == "S":
        # show stats
        print("Showing stats …")
    elif key_press == "M":
        # back to main menu
        print("Returning to menu")
        continue
    elif key_press == "Q":
        # break out of loop
        print("Quitting")
        break
    else:
        print("Unknown command. Try again")
# `break` takes us here
...

Sortir des boucles while Python im­bri­quées

L’uti­li­sa­tion de boucles im­bri­quées les unes dans les autres conduit ra­pi­de­ment à la confusion. Il est alors utile de sortir de la dernière boucle lancée à l’aide d’une ins­truc­tion break. Mais que se passe-t-il si l’on souhaite quitter une boucle située au-dessus ? Dans ce cas, il n’existe pas de mot-clé spé­ci­fique ; en principe, une solution au moyen d’une variable flag fonc­tionne.

Il est plus élégant de sortir des boucles while im­bri­quées en Python en combinant in­tel­li­gem­ment break, continue et else. Nous cons­trui­sons notre boucle afin que la boucle externe soit in­ter­rom­pue lorsqu’une ins­truc­tion break est exécutée dans la boucle interne. Pour ce faire, nous utilisons une ins­truc­tion continue à l’intérieur du bloc else interne afin de sauter le break externe si né­ces­saire :

# `continue` takes us here
while outer_cond:
    while inner_cond:
        ...
        if some_cond:
            print("Breaking out of inner loop")
            break
    # no inner `break` occured
    else:
        print("Continuing outer loop")
        # skip rest of outer loop body
        continue
    # we only get here if inner `break` occured
    print("Breaking out of outer loop")
    break
# outer `break` takes us here
...

Comment utiliser la boucle while dans Python ?

Dans la pratique, on trouve de nombreux cas d’uti­li­sa­tion de la boucle while en Python. En général, les boucles while sont utilisées pour des al­go­rithmes dont le nombre de ré­pé­ti­tions n’est pas déterminé à l’avance ou change pendant l’exécution. Les boucles while sont souvent utilisées en com­bi­nai­son avec d’autres struc­tures de contrôle telles que les em­bran­che­ments et les ins­truc­tions try-else. Con­si­dé­rons ces quelques exemples.

Utiliser une boucle while en Python dans une col­lec­tion

Pour itérer sur les éléments d’une col­lec­tion, la boucle for s’impose en Python. Du moins, c’est le cas si nous ne modifions pas la col­lec­tion à partir du corps de la boucle. Mais qu’en est-il si nous ef­fec­tuons des mo­di­fi­ca­tions en par­cou­rant les éléments ? Si nous voulons supprimer des éléments de la col­lec­tion lors de l’itération ?

Les boucles for peuvent provoquer des erreurs étranges si la col­lec­tion sous-jacente change pendant l’itération. La boucle while est la plus ap­pro­priée en Python. Nous utilisons la col­lec­tion di­rec­te­ment comme condition de la boucle while. Tant que la col­lec­tion contient des éléments, elle est évaluée comme vraie dans le contexte booléen. Si la col­lec­tion est vide, la boucle s’in­ter­rompt :

pieces = ['x', 'o', 'o', 'o', 'x', 'o', 'x', 'x']
while pieces:
    piece = pieces.pop()
    print(f"Removed {piece}")
# test
assert pieces == []

Im­plé­men­ter sa propre fonction range() avec la boucle while dans Python

Les boucles while de Python per­met­tent d’im­plé­men­ter des gé­né­ra­teurs. Un gé­né­ra­teur est une fonction qui utilise l’ins­truc­tion yield et qui génère des valeurs à la demande. Nous écrivons notre propre im­plé­men­ta­tion de la fonction range(). Nous utilisons l’ins­truc­tion yield dans une boucle while pour générer des nombres con­sé­cu­tifs. Lorsque l’ins­truc­tion yield est atteinte, une valeur est générée et la boucle se met en pause :

def my_range(start, stop):
    # only positive ranges implemented
    if stop <= start:
        return None
    current = start
    while current < stop:
        yield current
        # next call of next() continues here
        current += 1
# test
assert list(my_range(7, 9)) == list(range(7, 9))

Optimiser un modèle avec la boucle while de Python

Un modèle est calculé sur la base d’un ensemble de pa­ra­mètres. Les pa­ra­mètres sont ensuite ajustés et le modèle est recalculé. Une fonction cible permet d’estimer si la mo­di­fi­ca­tion des pa­ra­mètres a permis d’obtenir un meilleur modèle. Si c’est le cas, le processus est répété. Il est ainsi possible de trouver de manière itérative des pa­ra­mètres optimaux pour le modèle.

Nor­ma­le­ment, le modèle se stabilise après quelques ré­pé­ti­tions, de sorte que le progrès est de plus en plus faible. Si la pro­gres­sion tombe en dessous d’une certaine valeur seuil, nous in­ter­rom­pons l’op­ti­mi­sa­tion. Pour garantir que la boucle se termine, nous limitons en outre le nombre maximal de passages. Voici sché­ma­ti­que­ment une approche d’op­ti­mi­sa­tion de modèle utilisant une boucle while Python :

limit = 5
round = 0
progress = True
while progress and round < limit:
    # attempt next optimization
    round += 1
    # compute optimized parameters
    params = optimize(params)
    # make a copy of the old model
    old_model = model
    # compute new model using optimized parameters
    model = run(model, params)
    # worthwhile to further optimize?
    progress = has_improved(model, old_model)

Établir une connexion en Python avec la boucle while et try-except

L’éta­blis­se­ment d’une connexion peut échouer. Il est donc sou­hai­table d’im­plé­men­ter l’éta­blis­se­ment de la connexion avec plusieurs ten­ta­tives. Comme nous ne pouvons pas savoir à l’avance combien de ten­ta­tives seront né­ces­saires, nous utilisons une boucle while en Python. Nous limitons également le nombre maximal de ten­ta­tives. Si aucune des ten­ta­tives n’a réussi, nous nous arrêtons avec un message d’erreur.

Voici une solution sché­ma­tique : nous utilisons une ins­truc­tion try-except pour in­ter­cep­ter une erreur lors de l’éta­blis­se­ment de la connexion. L’uti­li­sa­tion d’une ins­truc­tion break dans le bloc try et d’une ins­truc­tion continue dans le bloc except nous permet de garantir une itération correcte. Si la connexion échoue, nous essayons à nouveau avec continue. Si la connexion est établie, nous terminons la boucle avec break :

max_tries = 10
attempt = 0
conn = None
# `continue` takes us here
while attempt < max_tries:
    attempt += 1
    print("Trying to get a connection")
    try:
        # might raise `ConnectionException`
        conn = get_connection()
        # got our connection
        break
    # `get_connection()` raised `ConnectionException`
    except ConnectionException:
        print("Something went wrong. Trying again")
        continue
# went through `max_tries` unsuccessful connection attempts
else:
    assert conn is None
    print("Unable to connect")
# `break` takes us here
assert conn is not None
print("Connection established")

Utiliser la boucle while de Python pour itérer sur des struc­tures ré­cur­sives

La boucle while de Python est bien adaptée à la ré­so­lu­tion de problèmes récursifs. Ainsi, la boucle convient pour itérer sur :

    • Des listes im­bri­quées
    • Des struc­tures ar­bo­res­centes
    • Des graphes

Voyons comment cela fonc­tionne avec l’exemple d’une poupée Ma­trio­chka. Ce jouet bien connu est composé de poupées im­bri­quées les unes dans les autres. De l’extérieur, le nombre de niveaux n’est pas visible. Au lieu de cela, nous procédons de manière itérative : nous ouvrons la poupée la plus ex­té­rieure et regardons ce que nous trouvons à l’intérieur. S’il s’agit d’une autre ma­trio­chka, nous répétons le processus. C’est exemple est un cas typique d’uti­li­sa­tion d’une boucle while.

Nous mo­dé­li­sons la ma­trio­chka comme une liste imbriquée avec un seul élément à la fois. Soit une autre liste est incluse, soit un objet qui n’est pas une liste. Nous itérons sur la ma­trio­chka tant qu’elle est une liste. A l’intérieur du corps de la boucle, nous utilisons une af­fec­ta­tion pour descendre d’un niveau. A un moment donné, nous trouvons un élément qui n’est pas une liste. Nous avons alors trouvé l’objet final qu’il contient et in­ter­rom­pons l’itération :

def open_matroshka(matroshka):
    """
    * Matroshka dolls stacked five levels deep, with `None` inside:
    `matroshka = [[[[[None]]]]]`
    """
    while type(matroshka) is list:
        print("Opening the next matroshka")
        # go one level deeper
        matroshka = matroshka.pop()
    else:
        print(f"Reached the bottom and found {matroshka}")
        return matroshka
# test
matroshka = [[[[[None]]]]]
assert open_matroshka(matroshka) is None

Cette approche simple fonc­tionne quelle que soit la pro­fon­deur des poupées im­bri­quées. Notre al­go­rithme creuse jusqu’au fond et fait ap­pa­raître l’objet qu’il contient. C’est toute la magie de la boucle while de Python en action.

Se déplacer sur un plateau de jeu avec une boucle while Python

Une uti­li­sa­tion fréquente des boucles while en Python consiste à déplacer une pièce sur un plateau de jeu. Si l’on veut être sûr d’aller sur toutes les cases, il faut deux boucles im­bri­quées l’une dans l’autre. Il faut alors tenir compte du temps d’exécution : pour les grands espaces, le programme peut être très long à exécuter.

Nous mettons en œuvre une simple « marche aléatoire », dans laquelle une pièce est déplacée de manière aléatoire sur le plateau de jeu jusqu’à ce qu’elle se trouve à une des­ti­na­tion. Un tel modèle de mouvement se retrouve par exemple dans le dé­pla­ce­ment d’une particule dans un liquide ou d’une mouche qui vole dans l’espace. Comme nous ne savons pas à l’avance combien d’ité­ra­tions seront né­ces­saires, nous utilisons une boucle while Python.

Nous dé­fi­nis­sons d’abord la fonction random_walk(), qui contient la boucle while. Avec l’opérateur Python, nous vérifions si la position actuelle est la des­ti­na­tion. Si ce n’est pas le cas, l’itération se poursuit :

def random_walk(board = (10, 10), goal = (4, 4), start = (0, 0)):
    # ensure arguments are valid
    if not (goal[0] in range(board[0]) and goal[1] in range(board[1]) and start[0] in range(board[0]) and start[1] in range(board[1])):
        print(f"Goal {goal} and / or start position {start} outside of board with dimensions {board}")
        return None, 0
    steps = 0
    pos = start
    # as long as we haven't reached the goal
    while not pos == goal:
        # move to neighboring position
        pos = get_neighbor(pos, board)
        steps += 1
        print(f"Moved to position {pos}")
    print(f"Reached goal at {pos} after {steps} steps")
    return pos, steps

Nous dé­fi­nis­sons également une fonction auxi­liaire get_neighbor() qui renvoie un champ possible autour d’une position donnée :

def get_neighbor(pos, bounds):
     from random import choice
     """
          x = 0     . . .     m
                - - - - - - - -
      y = 0 |
              |
            . |              (x, y-1)
            . |  (x-1, y) (x, y)  (x+1, y)
            . |              (x, y+1)
              |
            n |
    
     """
     x, y = pos
     # computer neighbors
     neighbors = [ (x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1) ]
     # filter out neighbors that are outside of bounds
     neighbors = [ pos for pos in neighbors if 0 <= pos[0] < bounds[0] and 0 <= pos[1] < bounds[1] ]
     # select a random neighbor
     neighbor = choice(neighbors)
     return neighbor

Ensuite, nous testons l’im­plé­men­ta­tion de notre Random Walk :

random_walk(board = (10, 10), goal = (4, 4), start = (5, 7))
Aller au menu principal