3.5. Structures de contrôle

3.5.1. Construction de prédicats

Un prédicat est une expression qui possède une valeur de vérité : par exemple, “La variable x est un nombre plus grand que deux” est un prédicat, qui pourra être vrai ou faux selon la valeur de la variable, mais, dans tous les cas, le résultat sera sans ambigüité. Par contre, la phrase “La variable x est grande” n’est pas un prédicat, car il n’y a pas de comparaison.

3.5.1.1. Valeur de vérité des objets

Tout objet en Python possède naturellement une valeur de vérité. En fait, tous les objets sont équivalents à “vrai”, sauf les suivants :

  • objets particuliers : False, None ;
  • les conteneurs vides : (), [], {} (dictionnaire), '', etc. ;
  • les nombres qui sont zéros : 0, 0.0, 0j, etc.

Afin de simplifier les termes, on dira simplement qu’un objet est vrai/faux, même si ce n’est qu’une équivalence. On peut vérifier cette particularité avec la fonction bool() :

>>> bool([])
False
>>> bool([1, 2])
True
>>> 1 is True
False
>>> 1 == True
True

3.5.1.2. Combinaison d’expressions

L’opérateur not retourne True si l’expression qui suit est fausse, et inversement, elle retourne False si ce qui vient après est vrai :

>>> not True
False
>>> not False
True

Finalement, les deux mots-clés and et or permettent de combiner plusieurs expressions :

  • x and y : l’expression x est d’abord évaluée et sa valeur est retournée si elle est fausse. Sinon, y est évaluée et sa valeur est retournée.
  • x or y : l’expression x est d’abord évaluée et sa valeur est retournée si elle est vraie. Sinon, y est évaluée et sa valeur est retournée.

Ces relations sont résumées dans les deux tableaux suivants (le second traduit le premier en termes de valeurs de vérité).

A B A and B A or B
False False A B
True False B B
False True A A
True True B A
A B A and B A or B
False False False False
True False False True
False True False True
True True True True

Il est important de noter que la valeur retournée n’est pas convertie en booléen. Cette construction est utile par exemple si on veut attribuer une valeur par défaut à un objet vide :

>>> True and False
False
>>> s = '' or 'défaut'
>>> s
'défaut'
>>> 1 or 2
1
>>> 1 and 2
2

3.5.1.3. Comparaisons

Un opérateur permet de comparer deux objets, selon différents critères. Voici la liste de ces opérateurs, ainsi que leur signification :

  • a is b : vrai si a est identique à b ;
  • a is not b : vrai si a n’est pas identique à b ;
  • a == b : vrai si a est égal à b ;
  • a != b : vrai si a est différent de b ;
  • a > b : vrai si a est plus grand que b ;
  • a < b : vrai si a est plus petit que b ;
  • a >= b : vrai si a est plus grand ou égal à b ;
  • a <= b : vrai si a est plus petit ou égal à b.

Note

En ce sens, les deux mots-clés in et not in peuvent être vus comme des opérateurs de comparaison.

Le symbole d’égalité == dénote une égalité superficielle, dans le sens où deux objets peuvent être égaux mais pas identiques :

>>> 1.0 is 1
False
>>> 1.0 == 1
True

La même distinction existe entre is not et !=.

Les opérateurs ont tous le même ordre de priorité, qui est plus faible que celle des opérateurs arithmétiques (+, -...), et ils seront donc lus de la gauche vers la droite : il est permis d’écrire a op1 b op2 c (où a, b, et c sont des expressions, op1 et op2 sont des opérateurs), qui sera interprété comme ``a op1 b and b op2 c :

>>> a = 3
>>> 1 < a < 10
True
>>> 1 < a > 2
True

La dernière ligne peut paraitre étrange, mais elle est tout à fait valable : en effet, elle est interprétée comme 1 < a and a > 2.

3.5.1.4. Conclusion

Les différents opérateurs de combinaison d’expressions permettent de construire des prédicats complexes à partir de prédicats plus simples, comme des comparaisons ou des opérations arithmétiques. Ces prédicats jouent réellement le rôle de booléen et peuvent être utilisés partout là où un booléen est attendu.

3.5.2. Condition — if

Une condition if (“si” en anglais) permet d’interpréter ou non une suite d’instructions en fonction de la valeur de vérité d’une expression [1].

>>> if False:
...     print(1)
>>> if True:
...     print(2)
2

Il est possible de construire des conditions plus évoluées grâce aux mots-clés elif (contraction de “else if” : “sinon si”) et else (“sinon”). Les conditions elif sont évaluées tour à tour jusqu’à ce que l’une soit vérifiée. Si aucune ne l’est, alors les instructions du bloc else sont évaluées :

>>> a = 3
>>> if a > 4:
...     print('"a" is higher than 4')
... elif a > 2:
...     print('"a" is higher than 2 and lower than or equal to 4')
... elif a > 0:
...     print('"a" is higher than 0 and lower than or equal to 2')
... elif a == 0:
...     print('"a" is equal to 0')
... else:
...     print('"a" is lower than 0')
"a" is higher than 2 and lower than or equal to 4

N’hésitez pas à changer la valeur de a et à tester à nouveau.

3.5.3. Boucle conditionnelle — while

Une boucle conditionnelle while (“tant que”) exécute une suite d’instructions tant qu’une condition vaut vraie.

>>> a = 0
>>> while a < 5:
...     print(a)
...     a += 1
0
1
2
3
4

Warning

Dans le cas où vous auriez indiqué un prédicat tout le temps vrai — on parle de boucle infinie —, il est possible d’interrompre la boucle en appuyant sur Ctrl+C. S’il s’agit d’une interface graphique, alors il est nécessaire de demander au gestionnaire des programmes de tuer le processus en question.

Toutefois, il existe des situations les boucles infinies peuvent s’avérer utile, à condition qu’elle puise être interrompue quand leur rôle est terminé. Nous y reviendrons à la fin du chapitre car il nous manque encore un outil important : le mot-clé break.

3.5.4. Itération — for

La boucle for est construite sur le modèle for obj in cont (“pour … dans …” en anglais). Elle consiste à sélectionner un à un les objets du conteneur cont, en les plaçant successivement dans la variable obj. Ainsi, à chaque itération, cette variable prend une valeur différente, qui est celle d’un objet du conteneur.

>>> for i in ['a', 'b', 'c']:
...   print(i)
a
b
c

Warning

Dans le cas particulier d’une séquence, les objets sont parcourus dans l’ordre, mais il n’est pas possible de prédire l’ordre de parcours pour un conteneur général, comme un dictionnaire, car aucun ordre n’est défini.

Il est fortement déconseillé de modifier les objets sur lesquels on itère dans une boucle for, car le risque est grand que l’interpréteur ne fasse pas ce que l’on attendait de lui, ou même qu’il lève une exception RuntimeError :

>>> dic = {'a': 1, 'b': 2}
>>> for i in dic:
...     del dic[i]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

Ici, l’interpréteur est mécontent car la taille du dictionnaire a changé au cours de la boucle, et il ne sait plus où il en est.

Il est nécessaire de faire une petite digression afin de présenter l’inestimable fonction range(ini, fin)(), qui crée un objet représentant les entiers de ini à fin-1 (la structure détaillée est donnée dans la section Fonctions built-in) :

>>> range(3, 10)
range(3, 10)
>>> list(_)
[3, 4, 5, 6, 7, 8, 9]

Combinée avec la structure for ... in, elle remplace avantageusement la boucle for (à la syntaxe parfois compliquée) des autres langages, qui permet seulement d’énumérer une liste de nombres :

>>> for i in range(5):
...     print(i)
0
1
2
3
4

3.5.5. Autres techniques pour les boucles

Les mots-clés continue et break permettent d’influencer le déroulement des boucles for et while, et un bloc else peut leurs être ajoutées.

3.5.5.1. Continue

Le mot-clé continue force l’interpréteur à revenir au début de la boucle et à entamer l’itération suivante, sans exécuter les instructions qui suivent ce mot-clé.

>>> for i in range(4):
...     if i == 2:
...         continue
...     print(i)
0
1
3

Dans cet exemple, on voit que lorsque la variable i vaut 2, la condition devient vraie et l’instruction print(i) est ignorée.

3.5.5.2. Break

Le mot-clé break permet d’interrompre l’exécution d’une boucle, quel que soit son avancement.

>>> for i in range(20):
...     if i == 4:
...         break
...     print(i)
0
1
2
3

Nous voyons que dès que i prend la valeur 4, l’instruction break termine l’exécution de la boucle même si l’affichage des nombres aurait dû se poursuivre jusqu’à 19.

3.5.5.3. Else

Il est possible d’ajouter une clause else finale à une boucle. Le bloc d’instructions ne sera exécuté que si la boucle s’est terminé normalement, c’est à dire qu’aucune instruction break n’a été rencontrée [2].

Voici un premier cas où la boucle se termine et où la clause else est exécutée :

>>> for i in range(6):
...     print(i)
... else:
...     print("end")
0
1
2
3
4
5
end

Maintenant, regardons un exemple où la boucle est arrêtée :

>>> for i in range(6):
...     if i == 4:
...         break
...     print(i)
... else:
...     print("end")
0
1
2
3

3.5.6. Instruction pass

pass ne fait strictement rien. Il est utilisé lorsque l’interpréteur attend un bloc de code mais que l’on ne souhaite rien écrire pour un temps, ou ne rien faire. Nous verrons son utilisation pratique dans le cadre des Erreurs et exceptions, des Fonctions et des Classes.

3.5.7. Imbrications

Rien n’empêche d’imbriquer plusieurs structures de contrôle (ou blocs) dans les autres, tant que l’indentation est respectée. Le code suivant affiche la liste des nombres pairs ou impairs compris entre 0 et 10 (exclus) selon que la variable n soit pair ou impair :

>>> n = 12
>>> for i in range(1, 10):
...     if (n % 2) == 0 and (i % 2) == 0:
...         print(i)
...     elif (n % 2) != 0 and (i % 2) != 0:
...         print(i)
2
4
6
8

Les parenthèses autour des opérations de modulo auraient pu être omises, comme l’opérateur de modulo évalué avant l’opérateur d’égalité. L’opérateur and est évalué en dernier.

En principe on évitera d’imbriquer plus de trois ou quatre afin de garder une certaine lisibilité du code, d’autant plus que, dans ces cas-là, il existe des manières plus élégantes de disposer le code.

Avant de conclure ce chapitre, donnons un exemple typique de boucle infini : l’affichage d’un menu dans un programme en ligne de commande, qui propose différentes actions à l’utilisateur. Ce dernier choisit une action à effectuer en appuyant sur une touche, et, une fois l’action terminée, il est ramener au menu. Et ainsi de suite jusqu’à ce qu’il appuie sur une touche permettant de quitter le programme (nous omettons les indications du prompt) :

while True:
    print('Menu')
    print('Action A : touche A')
    print('...')
    print('Action F : touche F')
    print('Quitter : touche Q')
    choice = input('Quel est votre choix ? ')
    print()

    if choice == 'A':
        print("Exécuter l'action A")
    if choice == 'F':
        print("Exécuter l'action F")
    if choice == 'Q':
        print("Fin du programme")
        break
    print()

Notez la présence des guillemets au lieu de l’apostrophe, ce qui permet d’écrire « l’action » sans devoir échapper l’apostrophe après le “l”. La présence des print() sans argument permet de sauter une ligne.

3.5.8. Exceptions

Les exceptions sont une sorte de structures de contrôle, mais un chapitre entier leur est réservé du fait de leur spécificité. Ainsi, pour plus de détails, se reporter au chapitre Erreurs et exceptions.

3.5.9. Résumé

Dans ce chapitre nous avons vu comment créer des prédicats et nous en servir pour construire des blocs conditionnels et des boucles, que l’on a ensuite imbriqués.

3.5.10. Exercices

  1. Écrire une boucle qui calcule la somme des dix premiers carrés (c’est à dire 1² + 2² + … + 9²).

  2. Affichez à l’écran les quinze premiers multiples d’un nombre que vous choisirez.

  3. Écrire une boucle qui affiche tous les nombres divisibles par 3 et par 5 dans un intervalle que vous choisirez.

  4. Calculer la longueur d’une chaine de caractères (sans utiliser la fonction len()).

  5. Écrire une boucle qui permet de compter le nombre de voyelles dans une chaine de caractères.

  6. Créer un programme qui doit faire deviner au joueur un nombre entre 1 et 100 tiré aléatoirement (on pensera à utiliser le module random). Après chaque entrée, on indiquera au joueur si le nombre est plus grand ou petit.

    On pourra envisager d’ajouter un compteur de tentatives.

  7. Écrire un algorithme semblable au précédent, en inversant les rôles : le joueur choisit un nombre entier dans un certain intervalle, et l’ordinateur doit deviner ce dernier.

  8. Améliorer le programme précédent pour faire deviner un nombre à virgule. L’ordinateur pourra-t-il deviner précisément le nombre ? Penser à adapter l’algorithme.

Footnotes

[1]En vertu de ce qui a été dit dans la conclusion de la section précédente, le True et le False peuvent être remplacés par n’importe quel prédicat.
[2]Elles permettent donc, entre autres, de faire ce que font les boucles foreach de langages tel que le PHP.
[3]Notons qu’une exception, si elle est interceptée par un bloc try-except n’interrompt par le bon déroulement d’une boucle (dans le cas contraire, elle interrompt le programme dans son ensemble, donc la clause else ne sera évidemment pas exécutée).