Tests logiques

Dans le tutoriel précédent, nous avons évoqué – sans l’expliciter – la notion de test, à travers l’exemple des tests d’appartenance. A présent, nous allons entrer dans le détail du fonctionnement des tests logiques en Python. Ces derniers sont un outil essentiel dans la création de programmes permettant d’automatiser des opérations, dans la mesure où ils permettent d’exécuter – ou non – du code selon certaines conditions. Ils permettent donc à l’ordinateur de prendre des décisions selon des critères fixés par l’utilisateur.

Le type Booléen

Dans sa plus simple forme, un test en Python est une expression qui évalue à “vrai” ou “faux”. Par exemple, l’expression \(3 > 2\) est vraie, le test associé renverra donc “vrai”. Pour ce type d’évaluation, Python dispose d’un type d’objets particulier : les Booléens. Contrairement aux types d’objet que nous avons déjà vus (int, float, str..), les Booléens ne peuvent prendre que deux valeurs : True et False.

type(True)
bool

Comme n’importe quel objet, les Booléens peuvent être assignés à des variables.

a = False
print(a)
print(type(a))
False
<class 'bool'>

Les valeurs True et False doivent être écrites de cette manière précisément (première lettre en majuscule, pas de guillemets). Elles ne peuvent par ailleurs pas être utilisées comme noms de variable afin de limiter les ambiguïtés.

a = true  # Python chercher la variable `true` mais elle n'existe pas
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[3], line 1
----> 1 a = true  # Python chercher la variable `true` mais elle n'existe pas

NameError: name 'true' is not defined
True = 3
  Cell In[4], line 1
    True = 3
    ^
SyntaxError: cannot assign to True

Opérateurs de comparaison

Les opérateurs de comparaison formalisent les opérations mathématiques de comparaison (égalité, non-égalité, inégalités). Ils comparent deux valeurs et renvoient une valeur booléenne.

Opérateur Signification
== Egal à
!= Non égal à
< Strictement inférieur à
> Strictement supérieur à
<= Inférieur ou égal à
>= Supérieur ou égal à

Illustrons ces opérateurs à l’aide de quelques exemples.

3 == 3
True
63 == 36
False
2 != 3
True
2 != 2
False
3 > 2
True
a = 36
a <= a
True
a < a
False

Tout semble fonctionner correctement pour des opérations mathématiques usuelles. Mais ces opérateurs fonctionnent en réalité sur n’importe quel type d’objet.

'do re mi fa sol' == 'do re mi fa sol'
True
'do re mi fa sol' == 'Do Re Mi Fa Sol'
False
'canard' != 'abeille'
True
True == True
True
True == False
False
[1, 2, 3] == [1, 2, 3]
True
[1, 2] != [3, 4]
True

Enfin, il est possible de réaliser des comparaisons en chaîne. L’expression renvoie True à condition que chacune des comparaisons soit vraie.

5 < 7 <= 8
True
5 < 7 <= 6
False

Opérateurs booléens

Les opérateurs booléens permettent de tester simultanément plusieurs expressions logiques. Fondamentalement, ces opérateurs prennent en entrée deux valeurs booléennes, et renvoient une unique valeur booléenne selon des règles de logique fixées. Ces règles sont énoncées dans des tables de vérité.

Opérateur and

Le premier opérateur booléen est and. Regardons sa table de vérité :

Expression Evaluation
True and True True
True and False False
False and True False
False and False False

Vérifions ces règles en pratique à l’aide de quelques exemples.

True and True
True
False and True
False

Les règles ont l’air de fonctionner sur les valeurs booléennes. Bien entendu, en pratique, on s’intéresse plutôt à évaluer de vraies expressions logiques. On peut donc utiliser ces opérateurs pour tester des expressions qui renvoient une valeur booléenne.

(3 > 2) and (5 <= 9)
True
a = ("x" != "z")
b = ("x" == "y")
a and b
False

Notez l’usage des parenthèses pour délimiter les tests : elles ne sont pas obligatoires, mais fortement recommandées dans la mesure où elles améliorent grandement la lisibilité tes tests.

L’opérateur or

Le second opérateur booléen est or. Sa table de vérité est la suivante :

Expression Evaluation
True or True True
True or False True
False or True True
False or False False
True or True
True
False or True
True
(3 > 2) or (5 <= 9)
True
a = ("x" != "z")
b = ("x" == "y")
a or b
True

L’opérateur not

Le dernier opérateur booléen est not. Sa table de vérité est la suivante :

Expression Evaluation
not True False
not False True
not True
False
not False
True
not (3 + 3 == 6)
False
not (7 < 7)
True

Structures conditionnelles

Toutes les expressions que nous avons vues précédemment sont des expressions booléennes : un test est effectué, et l’opération renvoie True or False selon que l’expression évaluée est vraie ou non. Dans le cadre d’un programme informatique qui réalise des opérations automatisées, on va vouloir les utiliser comme des conditions : si l’expression est vraie, alors l’ordinateur doit effectuer telle ou telle opération. Les structures conditionnelles permettent précisément cet usage.

Illustrons ce principe en implémentant le programme suivant :

  • Soit une variable x.

  • Si x est supérieur à \(5\), alors imprimer dans la console le message “L’expression est vraie.”

  • Dans le cas contraire, imprimer dans la console le message “L’expression est fausse.”

Faîtes varier la valeur de x pour vérifier le bon fonctionnement du test.

x = 7

if x >= 5:
    print("L'expression est vraie.")
else:
    print("L'expression est fausse.")
L'expression est vraie.

Blocs d’instruction et indentation

L’exemple précédent illustre la syntaxe des structures conditionnelles en Python. Ces structures sont basées sur des blocs d’instructions, qui délimitent l’ensemble des instructions qui doivent être exécutées lorsqu’un test est vrai. Les structures conditionnelles ont trois règles :

  • la ligne qui spécifie le test se termine par :

  • toutes les instructions qui doivent être exécutées si le test est vrai se situent à un même niveau d’indentation ;

  • la structure conditionnelle se termine lorsque l’indentation revient à son niveau d’origine.

Notons que les structures conditionnelles peuvent tout à fait être imbriquées, ce qu’illustre l’exemple suivant.

x = 7

if x >= 5:
    print("L'expression 1 est vraie.")
    if x >= 12:
        print("L'expression 2 est vraie.")
L'expression 1 est vraie.

Lorsque x = 7, le premier test renvoie True, le bloc d’instructions au niveau d’indentation 1 est donc exécuté ligne par ligne. Le second test renvoie quant à lui False, le bloc d’instructions au niveau d’indentation 2 n’est pas exécuté.

Faîtes varier la valeur de x pour que les deux blocs soient exécutés.

Instructions if, else et elif

Dans les structures conditionnelles, les tests peuvent être spécifiés à l’aide de trois instructions : if, else et elif. Les exemples précédents ont déjà illustré le fonctionnement des deux premières (et plus fréquentes) instructions.

En cas d’un test simple (une seule condition), on n’utilisera qu’une instruction if, dont le fonctionnement est simple : si la condition (test) renvoie True, alors le bloc d’instructions (indenté) qui suit est exécuté. Si la condition renvoie False, il ne se passe rien. Illustrons cela avec un test d’appartenance, dont nous avons vu des exemples dans le tutoriel précédent.

client = "Isidore"

if client in ["Alexandrine", "Achille", "Colette"]:
    print("Client connu.")

En pratique, on souhaite souvent spécifier une alternative, lorsque la condition de l’instruction if renvoie False. L’instruction else permet de spécifier un bloc d’instructions alternatif.

client = "Isidore"

if client in ["Alexandrine", "Achille", "Colette"]:
    print("Client connu.")
else:
    print("Client inconnu.")
Client inconnu.

Enfin, on peut vouloir spécifier plusieurs alternatives. Dans ce cas, on va utiliser des instructions elif. La première instruction elif ne va s’éxécuter que si le test de l’instruction if renvoie False. La seconde instruction elif ne va s’exécuter que si le test de la première instruction elif renvoie False, et ainsi de suite. Là encore, on peut spécifier une instruction finale else, qui ne s’exécute que si aucun des tests précédents n’a renvoyé True.

client = "Isidore"

if client == "Alexandrine":
    print("Bonjour Alexandrine.")
elif client == "Achille":
    print("Bonjour Achille.")
elif client == "Colette":
    print("Bonjour Colette.")
else:
    print("Bonjour cher inconnu.")
Bonjour cher inconnu.

NB : les instructions précédentes ont seulement valeur d’exemple. En pratique, il y a des manières beaucoup plus concises de coder un programme qui effectue les mêmes opérations.

Exercices

Questions de compréhension

  • 1/ Quelle est la particularité des Booléens par rapport aux autres types d’objets de base en Python ?

  • 2/ Quels sont les inputs et les outputs d’un opérateur de comparaison ?

  • 3/ Quels types d’objets peut-on comparer à l’aide d’un opérateur de comparaison ?

  • 4/ Quelle est la différence entre l’opérateur = et l’opérateur == ?

  • 5/ Quels sont les inputs et les outputs d’un opérateur booléen ?

  • 6/ Expliquer en français le principe de l’opérateur booléen and. Mêmes questions pour or et not.

  • 7/ Quelle est la différence entre expression booléenne et condition ?

  • 8/ Quelle est la structure d’une instruction conditionnelle ?

  • 9/ Peut-on imbriquer les instructions conditionnelles ?

  • 10/ Parmi les instructions if, else, et elif, lesquelles sont obligatoires et lesquelles sont facultatives ?

Afficher la solution

1/ Ils n’ont que deux valeurs : True et False. Les autres types ont une infinité de valeurs possibles.

2/ Inputs : deux valeurs. Output : valeur booléenne.

3/ Tous types d’objets. En pratique cependant, il n’y a pas beaucoup de sens à comparer des objets de type différent, le résultat sera généralement False.

4/ L’opérateur = assigne une valeur à une variable. L’opérateur == teste l’égalité de deux objets.

5/ Inputs : deux valeurs booléennes, ou deux expressions qui renvoient des booléens. Output : valeur booléenne.

6/ L’opérateur and renvoie True si ses deux inputs valent True, et False dans tous les autres cas. L’opérateur or renvoie True si au moins un de ses deux inputs vaut True, et False dans le cas où ils valent tous les deux False. L’opérateur not renvoie False si son input est True, et True sinon.

7/ Dans les deux cas, il s’agit de tests. On parle de condition lorsque les expressions sont utilisées dans le cadre des structures conditionnelles.

8/ L’instruction conditionnelle commence par une instruction if, else ou elif, qui se termine par :. Vient ensuite, indenté de un niveau, un bloc d’opérations qui ne s’exécutent que si l’instruction vaut True. Le bloc se termine lorsque l’indentation revient à son niveau initial.

9/ Oui, les instructions conditionnelles peuvent s’imbriquer à l’infini (en théorie) Il faut simplement faire attention à respecter les niveaux d’indentation.

10/ Seule l’instruction if est obligatoire.

Prédiction de résultats de tests

Prédire le résultats des tests suivants, et vérifier vos prédictions :

  • 'Simon' in ['simon', 'oceane', 'veronique']

  • [1, 2, 3] == ['1', '2', '3']

  • 'x' != 'x'

  • (9 > 5) and (3 == 5)

  • (3 > 2 and 5 >= 1) or (5 <= 9 and 6 > 12)

  • not (9 > 2*3)

  • not (9 > (2*3))

  • not ((7 > 8) or (5 <= 5))

  • (True and True) or (True == False)

  • (not False) or (not True)

# Testez votre réponse dans cette cellule
Afficher la solution
  • 'Simon' in ['simon', 'oceane', 'veronique'] : False

  • [1, 2, 3] == ['1', '2', '3'] : False

  • 'x' != 'x' : False

  • (9 > 5) and (3 == 5) : False

  • (3 > 2 and 5 >= 1) or (5 <= 9 and 6 > 12) : True

  • not (9 > 2*3) : False

  • not (9 > (2*3)) : False

  • not ((7 > 8) or (5 <= 5)) : False

  • (True and True) or (True == False) : True

  • (not False) or (not True) : True

Prédiction de test imbriqué

Considérons le programme écrit dans la cellule suivante.

x = 10

if True:
    print("Initialisation.")
    l = []
    if x > 8:
        l.append("a")
    elif x >= 2:
        l.append("b")
    else:
        l.append("c")
    if x - 6 < 0:
        print("Négatif.")
        
print(l)
Initialisation.
['a']

Pour les valeurs suivantes :

  • x = 1

  • x = 5

  • x = 10

prédire les résultats du programme :

  • que vaut l à la fin du programme ?

  • qu’est ce qui est imprimé dans la console au fil du programme ?

Vérifiez vos résultats.

# Testez votre réponse dans cette cellule
Afficher la solution
  • x = 1 : l = ['c'] et messages imprimés : ‘Initialisation’ et ‘Négatif’

  • x = 5 : l = ['b'] et messages imprimés : ‘Initialisation’ et ‘Négatif’

  • x = 10 : l = ['a'] et messages imprimés : ‘Initialisation’

Trois et pas plus

Ecrire un programme qui réalise les opérations suivantes :

  • Définir une liste qui contient 4 prénoms

  • Ecrire un test qui affiche le message (‘Trop de monde.’) si la liste contient plus de trois personnes

  • Supprimer une personne de la liste (en utilisant la fonction del ou la méthode pop vues dans un tutoriel précédent)

  • Réaliser le test à nouveau, il ne devrait plus y avoir d’output.

# Testez votre réponse dans cette cellule
Afficher la solution
people = ["Romuald", "Ursula", "Jean-Vincent", "Philomène"]

if len(people) > 3:
    print('Trop de monde.')

print(people)    
people.remove("Jean-Vincent")
print(people)

if len(people) > 3:
    print('Trop de monde.')

Le juste prix

La fonction input permet de demander à l’utilisateur d’entrer une valeur dans le cadre d’un programme Python. La syntaxe est la suivante : x = input(). Lorsque cette commande est exécutée, l’utilisateur doit rentrer une valeur, qui est alors assignée à la variable x.

En utilisant input et les instructions if, elif et else, coder le programme suivant :

  • demander une valeur à l’utilisateur, qui sera stockée dans une variable p

  • si p est strictement inférieur à \(15\), imprimer (avec la fonction print) le message “trop bas !”.

  • si p est strictement supérieur à \(15\), imprimer le message “trop haut !”.

  • si p est égal à \(15\), imprimer le message “dans le mille !”

Attention, input renvoie par défaut une chaîne de caractère. Il faut donc convertir la valeur de p au format entier (via la fonction int) pour que le jeu fonctionne.

# Testez votre réponse dans cette cellule
Afficher la solution
p = input()
p = int(p)

if p < 15:
    print("trop bas !")
elif p > 15:
    print("trop haut !")
else:
    print("dans le mille !")

Evaluation booléenne d’objets divers

En Python, tous les objets s’évaluent à True or False dans le cadre d’un test conditionnel (if/else). La règle générale est que les objets qui sont zéro ou vides (ex : une liste vide, un dictionnaire vide) s’évaluent à False, et inversement. Mais il n’y a pas besoin de connaître ces règles par coeur : elles se retrouvent facilement en pratique ! Par exemple, on peut utiliser le test conditionnel suivant :

if "test":
    print("True.")
else:
    print("False.")
True.

Prédire à quelle valeur booléenne vont s’évaluer les objets suivants, et vérifier à l’aide de la syntaxe précédente.

  • 0

  • 1

  • 12

  • -1

  • ’’ (string vide)

  • ’ ’ (string contenant seulement un espace)

  • [] (liste vide)

  • [''] (liste contenant seulement un string vide)

  • {}

  • {-1}

# Testez votre réponse dans cette cellule
Afficher la solution
  • 0 : False

  • 1 : True

  • 12 : True

  • -1: True

  • '' (string vide): False

  • ' ' (string contenant seulement un espace): True

  • [] (liste vide): False

  • [''] (liste contenant seulement un string vide): True

  • {}: False

  • {-1}: True

Comparaisons en chaîne

Nous avons vu qu’il était possible de réaliser des comparaisons en chaîne, qui renvoient True à condition que chacune des comparaisons inclues soit vraie. Trouvez une manière de réécrire la comparaison en chaîne suivante à l’aide d’opérateurs booléens.

5 < 7 <= 8 < 18

# Testez votre réponse dans cette cellule
Afficher la solution
print(5 < 7 <= 8 < 18)

print(5 < 7 and 7 <= 8 and 8 < 18)

Une comparaison en chaîne peut se réécrire avec des opérateurs and. Logique : il faut que chaque comparaison soit vraie pour que l’ensemble le soit aussi. En pratique, la version avec les and est sans doute préférable pour la lisibilité.

Booléens et langage binaire

Les Booléens sont fortement liés au langage binaire, dans lequel le 1 correspond à “vrai” et le 0 à “faux”. On va vérifier si ce lien existe dans le contexte de Python. Pour ce faire :

  • calculer la “représentation en entier” de la valeur booléenne de votre choix à l’aide de la fonction int ;

  • utiliser des booléens dans le cadre de calculs mathématiques pour vérifier leur comportement dans ce contexte.

# Testez votre réponse dans cette cellule
Afficher la solution
print(int(True))  

Un Booléen évalué comme entier donne bien la valeur binaire associée.

print(True + 3)  
Les Booléens se comportent comme leur valeur entière associée dans les calculs.

Comparaisons de strings

Que renvoient les tests de comparaison de type inégalités appliqués à des chaînes de caractères ? Produire quelques exemples pour tester le comportement.

# Testez votre réponse dans cette cellule
Afficher la solution
print("a" > "b")
print("a" < "b")
print("abricot" > "avocat")
print("abricot" < "avocat")
print("1" > "2")
print("1" < "2")
print("A1" < "A2")

La relation d’ordre utilisée est l’ordre alphanumérique : chaque caractère est pris individuellement, et les ordres sont A < Z et 1 < 9.

Tests d’égalité sur les floats

Les tests d’égalité entre nombres réels (type float en Python) peuvent être trompeurs. Pour vous en convaincre, effectuez le test suivant : (6 - 5.8) == 0.2

Pour comprendre le résultat du test, effectuer le calcul du membre de gauche du test seul. Que remarquez-vous ?

Imaginez (sans forcément l’implémenter) un autre test, basé sur des inégalités, qui permettrait de tester l’égalité approchée.

# Testez votre réponse dans cette cellule
Afficher la solution
diff = 3 - 2.7

print(diff == 0.3)

print(diff)

En Python, les nombres flottants sont toujours des valeurs approchées. On peut donc avoir ce genre de surprise dans les calculs.

tolerance = 0.0001
new_test = (0.3 - tolerance) < diff < (0.3 + tolerance)
print(new_test)
Ce dernier test permet de tester l’égalité entre diff et 0.3 de manière approchée, en permettant une certaine tolérance dans la comparaison.