Outils Conditions

Identification

Infoforall

11 - Conditions


Nous abordons aujourd'hui une autre notion très importante pour réaliser des choses intéressantes : les instructions conditionnelles.

En une question : comment réaliser des choses différentes en fonction de la situation ?

Evaluation ✎ : questions 08-14-16-18-20

Documents de cours : open document ou pdf

1 - Booléens

Booléens

Pour comprendre les tests logiques, il faut savoir que les ordinateurs n'ont que deux choix possibles lorsqu'on leur donne une proposition à tester (du style "est-on en 2025 ?"). Il s'agit d'un booléen, d'après George Boole, fondateur de l'algèbre de Boole dans le milieu du 19ème siècle.

Un booléen ne peut valoir que True (pour VRAI) ou False (pour FAUX).

  • Si nous étions en 2025, le résultat de "Sommes-nous en 2025 ?" donnerait True.
  • Sinon, le résultat donnerait False.

D'un point de vue informatique, cela donne :

>>> annee = 2025 >>> annee == 2025 True >>> annee != 2025 False >>> annee > 2025 False >>> annee >= 2025 True >>> annee < 2025 False >>> annee <= 2025 True

01° Utiliser ces instructions dans la console pour comprendre le principe de l'évaluation du booléen obtenu.

>>> a = 12 >>> b = 20 >>> b > a True >>> b < a False >>> b == a False >>> b != a True

On peut même stocker le résultat d'une expression booléenne dans une variable. Cette variable contiendra alors soit True, soit False.

02-A° Formuler en français la question équivalente aux différentes expressions booléennes dont les résultats seront stockés dans les variables c, d, e et f.

>>> a = 12 >>> b = 20 >>> c = b > a >>> d = b < a >>> e = b == a >>> f = b != a

...CORRECTION...

>>> c = b > a Le contenu de b est-il strictement supérieur au contenu de a ? >>> d = b < a Le contenu de b est-il strictement inférieur au contenu de a ? >>> e = b == a Le contenu de b est-il identique au contenu de a ? >>> f = b != a Le contenu de b est-il différent du contenu de a ?

Les questions équivalentes sont :

02-B° Trouver le contenu stocké dans les différentes variables c, d, e et f.

Après avoir obtenu vos réponses, vérifier à l'aide de la console.

>>> a = 12 >>> b = 20 >>> c = b > a >>> d = b < a >>> e = b == a >>> f = b != a

...CORRECTION...

>>> a = 12 >>> b = 20 >>> c = b > a >>> c True >>> d = b < a >>> d False >>> e = b == a >>> e False >>> f = b != a >>> f True
Instruction ou expression ?

Une instruction va modifier l'état de la mémoire d'une façon ou d'une autre : on modifie le contenu d'une variable, on fait avancer la tortue, on modifie la couleur sur une fenêtre graphique...

Une expression est uniquement une sorte de lecture des états des variables de façon à produire un résultat.

Exemple d'une instruction d'affection utilisant une évaluation :

>>> c = b > a

On peut dire que b > a est une expression (et même une expression booléenne puisqu'elle va être évaluée à True ou False).

Par contre, c = b > a dans son ensemble est une instruction (une instruction d'affectation ici) puisqu'elle va modifier l'état de la variable c.

Exemple d'évaluation (la console affichant la valeur évaluée si on n'en fait rien, comme ici):

>>> "La" in 'Lalala' True

Exemple d'instruction :

>>> d = "La" in 'Lalala'

C'est bien une instruction puisque l'état de la mémoire est différent après application de cette instruction : on a créé ou modifié le contenu affecté à la variable d.

Nous allons pouvoir utiliser ces booléens pour réaliser des instructions conditionnelles : des instructions qui vont être réalisées uniquement si la condition est évaluée à VRAI.

2 - Instruction conditionnelle SI

Structure générale des instructions conditionnelles

Quelque soit le langage, on retrouve toujours une structure qui ressemble à ceci :

1 2 3 4 5 6 7 8 9 10 11
SI cette expression booléenne est vraie Instruction A1 Instruction A2 SINON SI cette autre expression booléenne est vraie Instruction B1 Instruction B2 SINON (dans tous les autres cas) Instruction C1 Instruction C2

03° Tester la fonction suivante en lui fournisant différents paramètres entre 1980 et 2010 par exemple.

1 2 3 4 5 6 7 8 9 10
def creer_message(reponse): '''Renvoie un string en fonction de la réponse de l'utilisateur''' annee_creation_python = 1991 if reponse > annee_creation_python: message = f"Python a été créé avant {reponse}" elif reponse < annee_creation_python: message = f"Python a été créé après {reponse}" else: message = f"Oui, Python est sorti pour la première fois en {reponse}" return message
>>> creer_message(1981) ??? >>> creer_message(1991) ??? >>> creer_message(2021) ???

Questions :

  1. La fonction renvoie-t-elle systématiquement la même réponse pour 1980, 1991 et 2050 ?
  2. Ligne 4 : comment est évaluée l'expression booléenne reponse > annee_creation_python si le paramètre reponse contient 1987 ? Va-t-on alors exécuter la ligne 5 ?
  3. Ligne 6 : comment est évaluée l'expression booléenne reponse < annee_creation_python si le paramètre reponse contient 1987 ? Va-t-on exécuter la ligne 7 ?
  4. Avec quel mot-clé se code le SI en Python ?
  5. Avec quel mot-clé se code le SINON SI en Python ?
  6. Avec quel mot-clé se code le SINON en Python ?

...CORRECTION...

On constate bien que la fonction renvoie des réponses différentes en fonction de la valeur que reçoit la fonction en entrée :

>>> creer_message(1981) 'Python a été créé après 1981' >>> creer_message(1991) 'Oui, Python est sorti pour la première fois en 1991' >>> creer_message(2021) 'Python a été créé avant 2021'

L'expression reponse > annee_creation_python est évaluée progressivement de cette façon :

  • reponse > annee_creation_python
  • 1987 > annee_creation_python
  • 1987 > 1991
  • False

On n'exécute donc pas la ligne 5 de ce bloc puisque l'expression booléenne n'est pas vraie.

L'expression reponse < annee_creation_python est évaluée progressivement de cette façon :

  • reponse < annee_creation_python
  • 1987 < annee_creation_python
  • 1987 < 1991
  • True

Du coup, on va exécuter la ligne 7 qui appartient à ce bloc.

Le SI se code avec le mot-clé if.

Le SINON SI se code avec le mot-clé elif.

Le SINON (tout court) se code avec le mot-clé else.

04° Que trouve-t-on à la fin de chacune des lignes contenant if, elif ou else ?

Comment sont positionnées les instructions conditionnelles par rapport aux mots-clés précédents ?

A quelle autre syntaxe cela vous fait-il penser ?

...CORRECTION...

On retrouve la structure d'une fonction.

On finit chaque condition par  : .

On tabule chaque instruction liée à une condition.

C'est l'indentation vers la droite qui permet à Python de comprendre les actions que vous voulez qu'il réalise si une condition est vérifiée. Il faut donc placer une tabulation ou 4 espaces de suite pour savoir qu’elles sont les instructions rattachées au bloc. De plus, on remarquera qu’on met un double point [ : ] après la condition de if, else if (elif en Python) ou else. Sans ce symbole, vous déclenchez une erreur. Ce double point permet de dire à l'interpréteur que la déclaration de la condition est finie.

Dans Thonny ou tout autre éditeur spécialisé dans Python, la tabulation est équivalente à 4 espaces. Attention par contre aux éditeurs multi-langages. La tabulation est parfois de 2 ou 3 espaces. Dans ce cas, votre code ne sera pas compris par l'interpréteur Python. Dans ce cas de figure, préférez les 4 espaces ou configurez votre éditeur pour forcer une tabulation équivalente à 4 espaces.

05° Utiliser l'animation ci-dessous en utilisant les trois paramètres qu'on demande à la fonction creer_message().

Question :

L'interpréteur continue-t-il à effectuer le reste des tests à partir du moment où il valide l'un des blocs d'instructions ?

1 2 3 4 5 6 7 8 9 10
def creer_message(reponse): '''Renvoie un string en fonction de la réponse de l'utilisateur''' annee_creation_python = 1991 if reponse > annee_creation_python: message = f"Python a été créé avant {reponse}" elif reponse < annee_creation_python: message = f"Python a été créé après {reponse}" else: message = f"Oui, Python est sorti pour la première fois en {reponse}" return message

CLIQUEZ SUR UN BOUTON-REPONSE :

reponse :

annee_creation :

condition :

...CORRECTION...

L'interpréteur ne traitera que l'un des blocs.

Dès qu'il rencontre une condition valide, il exécute les instructions correspondantes puis quitte la structure conditionnelle SANS tester les possibilités restantes.

Déroulement d'une séquence if-elif-else

A SAVOIR IMPERATIVEMENT

L'interpréteur Python ne va exécuter que l'un des blocs d'une structure if-elif-else.

Python commence par évaluer la condition du premier bloc.

Si la condtion est vraie, il exécute les instructions 2-3-4 de ce bloc et ne regardera absolument pas les autres conditions et blocs. Il passera directement à la ligne Instruction 9.

Instruction 1 (avant les instructions conditionnelles)
if reponse > annee_creation_python: Instruction 2 Instruction 3 Instruction 4
elif reponse < annee_creation_python: Instruction 5 Instruction 6 Instruction 7
else: Instruction 8 Instruction 9
Instruction 10 (après les instructions conditionnelles)

Si la première condtion est fausse, il va évaluer la deuxième condition. Si elle est vraie, il exécute les instructions 5-6-7 de ce bloc et ne regardera absolument pas le dernier bloc. Il passera directement à la ligne Instruction 9.

Si aucune des conditions précédentes n'est vraie, il va sinon réaliser les instructions 8-9 du dernier bloc. Le ELSE est donc le bloc par défaut. Celui qu'on exécute si aucune des conditions précédentes n'a pas être validée.

Je répète : La chose à bien comprendre : l'interpréteur Python ne va exécuter que l'un des blocs. Il s'arrête au premier test correct et ne s'occupe pas des autres situés en dessous, même si les conditions sont vraies également.

06° Compléter la fonction appreciation() ci-dessous. Elle doit renvoyer "Bien" si le paramètre note est strictement supérieur à 13, "Insuffisant" si le paramètre est inférieur à 9 et "Moyen" sinon.

1 2 3 4 5 6 7 8
def appreciation(note): '''Renvoie un string correspondant à l'appréciation voulue :: param note(str) :: une note dans [0;20] :: return (str) :: l'appréciation correspondante ''' pass

...CORRECTION...

Version un seul endroit où on gère l'envoi de la réponse.

1 2 3 4 5 6 7 8 9 10 11 12 13 14
def appreciation(note): '''Renvoie un string correspondant à l'appréciation voulue :: param note(str) :: une note dans [0;20] :: return (str) :: l'appréciation correspondante ''' if note > 13: app = "Bien" elif note < 9: app = "Insuffisant" else: app = "Moyen" return app

07° Quelqu'un propose cette version 02 (elle est valide) permettant de répondre dès qu'on sait quoi répondre plutôt que de traiter les différents cas et d'envoyer la réponse à la fin.

1 2 3 4 5 6 7 8 9 10 11 12 13
def appreciation_v2(note): '''Renvoie un string correspondant à l'appréciation voulue :: param note(str) :: une note dans [0;20] :: return (str) :: l'appréciation correspondante ''' if note > 13: return "Bien" elif note < 9: return "Insuffisant" else: return "Moyen"

D'un point de vue conceptuel, cette version est un peu moins bien que la précédente car elle possède plusieurs return, plusieurs points de sortie. Elle a néanmoins le mérite de ne pas nécessiter de continuer à lire la suite à partir du moment où on sait quoi répondre.

Une autre personne pense qu'on peut encore faire plus rapide en supprimant le else final avec cette version 03 (elle est valide également puisque si on arrive ici, c'est qu'on a rencontré aucun des return précédent).

1 2 3 4 5 6 7 8 9 10 11 12
def appreciation_v3(note): '''Renvoie un string correspondant à l'appréciation voulue :: param note(str) :: une note dans [0;20] :: return (str) :: l'appréciation correspondante ''' if note > 13: return "Bien" elif note < 9: return "Insuffisant" return "Moyen"

Un enthousiaste pense du coup à modifier la première version est propose une version 04 (version fausse cette fois).

1 2 3 4 5 6 7 8 9 10 11 12 13
def appreciation_v4(note): '''Renvoie un string correspondant à l'appréciation voulue :: param note(str) :: une note dans [0;20] :: return (str) :: l'appréciation correspondante ''' if note > 13: app = "Bien" elif note < 9: app = "Insuffisant" app = "Moyen" return app

Question

Expliquer pourquoi on peut se passer du else dans la version 3 mais pas dans la version 4.

...CORRECTION...

La version 03 fonctionne car dès qu'on rencontre un return, on répond et on sort de la fonction.

Si on atteint les lignes 9 ou 11, on quitte donc la fonction. Cela veut dire qu'on ne peut arriver à la ligne 12 que si la note n'est ni supérieure à 13, ni inférieure à 9.

Par contre, dans la version 04, il ne place pas de return. Cela veut dire que même si on rentre dans l'un des blocs if ou elif, on affectera la variable app mais on ne sortira pas de la fonction.

Du coup, on va simplement atterir ligne 12 à chaque fois et écraser la version précédente de notre variable !

Ici, le else est donc obligatoire.

IF dans une fonction

Puisqu'on sort d'une fonction dès qu'on rencontre return, il existe deux façons de créer une fonction qui répond différentes choses en fonction des conditions en utilisant une instruction conditionnelle :

Version propre avec une seule "porte de sortie"

  • Avantage : plus facile de vérifier la correction de la fonction ("son bon fonctionnement", nous verrons cela plus tard)
  • Désavantage : il faut lire séquentiellement la fonction jusqu'à trouver la "sortie" et il faut créer au moins une variable de stockage e la réponse.
  • 1 2 3 4 5 6 7 8
    def appreciation(note): if note > 13: app = "Bien" elif note < 9: app = "Insuffisant" else app = "Moyen" return app

Version avec plusieurs "portes de sortie"

  • Désavantage : moins facile de vérifier la correction de la fonction.
  • Avantage : on n'a pas besoin en tant que "lecteur" de continuer à lire le reste du code lorsqu'on rencontre une "sortie".
  • 1 2 3 4 5 6 7
    def appreciation(note): if note > 13: return "Bien" elif note < 9: return "Insuffisant" else return "Moyen"

    Ou encore (puisque si on arrive au dernier bloc c'est que les deux premiers n'ont pas été validés) :

    1 2 3 4 5 6
    def appreciation(note): if note > 13: return "Bien" elif note < 9: return "Insuffisant" return "Moyen"

Voici quelques autres exemples d'utilisation pour moins comprendre comment structurer vos demandes :

  • La fonction trop_gentil() qui renvoie une note augmentée de +2 aux notes inférieures à 10 et, sinon, +1 pour les notes inférieures à 19.
  • 1 2 3 4 5 6
    def trop_gentil(note): if note < 10: note = note + 2 elif note < 19: note = note + 1 return note
  • La fonction beaucoup_trop_gentil() qui renvoie une note augmentée de +5 aux notes inférieures à 10 et, sinon, +3 aux autres notes. La fonction ne doit jamais renvoyer de notes supérieures à 20.
  • 1 2 3 4 5 6 7 8 9 10
    def beaucoup_trop_gentil(note): # Première modification if note < 10: note = note + 5 else: note = note + 3 # Vérification avant envoi if note > 20: note = 20 return note
  • La fonction beaucoup_trop_gentil() pas top car elle possède plusieurs "portes de sortie" : vous devriez comprendre un peu mieux pourquoi on préfère n'en placer qu'un seul return:
  • 1 2 3 4 5 6 7 8 9 10 11 12 13 14
    def beaucoup_trop_gentil(note): # Première modification if note < 10: note = note + 5 # Vérification avant envoi if note > 20: note = 20 return note else: note = note + 3 # Vérification avant envoi if note > 20: note = 20 return note

Passons à la pratique :

✎ 08° Compléter la fonction traduction() ci-dessous. Elle doit renvoyer :

  • "vert" si le paramètre color est "green",
  • "rouge" pour "red",
  • "bleu" pour "blue",
  • "jaune" pour "yellow".
  • si la couleur est inconnue, elle doit répondre "traduction inconnue pour cette couleur".

Remarque : nous verrons plus tard dans l'année un moyen plus efficace de traiter ceci. Ici, vous allez devoir écrire une longue succession de tests...

1 2 3 4 5 6 7 8 9
def traduction(color): '''Renvoie un string correspondant à la traduction de la couleur ANG -> FR :: param color(str) :: une couleur en anglais :: return (str) :: la traduction en français du string précédent ''' couleur = "traduction inconnue pour cette couleur" return couleur

3 - Prédicat : fonction renvoyant un booléen

Nous avons déjà vu l'opérateur d'égalité ==. Il permet de tester si une variable contient bien 5 par exemple.

Attention à ne pas confondre

  • l'opérateur d'égalité == (deux signes =) et
  • l'opérateur d'affectation = (un seul signe =).

Avec a == b, l'interpréteur comprend qu'il doit tester l'égalité des variables a et b. Il va donc évaluer cette expression et obtenir vrai ou faux.

Avec a = b, l'interpréteur comprend qu'il faut stocker b dans la variable a.

Tester une égalité avec Python

Pour tester l'égalité, on utilise un double signe égal a == b.

Cette expression est équivalente à la question "Les contenus des variables a et b sont-ils identiques ?"

Prédicat

C'est simplement une fonction qui renvoie un booléen.

True ou False en Python.

On parle de prédicat puisque la fonction prédit une propriété à partir des paramètres reçus.

09° Compléter la fonction comparer() pour qu'elle renvoie True si les deux paramètres sont égaux.

1 2 3
def comparer(proposition, mystere): '''Renvoie True si les deux paramètres sont égaux, False sinon''' pass

...CORRECTION...

Il est probable que vous ayez tapé quelque chose qui ressemble à cela :

1 2 3 4 5 6
def comparer(proposition, mystere): '''Renvoie True si les deux paramètres sont égaux, False sinon''' if proposition == mystere: return True else: return False

Si vous avez mémorisé l'histoire du return qui provoque une sortie de la fonction, vous avez même surement tapé ceci :

1 2 3 4 5
def comparer(proposition, mystere): '''Renvoie True si les deux paramètres sont égaux, False sinon''' if proposition == mystere: return True return False

Et pourtant, il a eu encore une manière plus courte de répondre (les explications sont ci-dessous si vous ne comprennez pas bien comment ça fonctionne) :

1 2 3
def comparer(proposition, mystere): '''Renvoie True si les deux paramètres sont égaux, False sinon''' return proposition == mystere

Ce cas de figure est très courant en informatique : créer un prédicat (une fonction qui renvoie True ou False) ne requiert donc forcément pas d'utiliser un if : il suffit de fournir l'expression qu'on veut évaluer derrière votre return.

Exemple avec une expression qui évalue si une note de 10 permet de valider un semestre :

>>> note = 9 >>> note >= 10 False >>> note = 12 >>> note >= 10 True

Du coup, la fonction donnerait simplement ceci :

1 2 3
def valider(note): '''Renvoie True si la note est supérieure ou égale à 10''' return note >= 10

10° Evaluer note >= 10 si le paramètre note vaut 7. Que va renvoyer la fonction valider() ?

...CORRECTION...

note >= 10

7 >= 10

False

11° Un élève propose cette version : est-elle bonne, fausse, correcte mais inutilement longue ?

1 2 3 4 5 6
def valider(note): '''Renvoie True si la note est supérieure ou égale à 10''' if note >= 10: return True else: return False

...CORRECTION...

Bonne mais inutilement longue.

Si vous préférez cette version pendant quelques temps, utilisez la. Vous aurez les points.

Mais dès que vous serez à l'aise, passez plutôt à la version courte return note >= 10.

4 - Inversion d'un booléen avec not

Nous allons voir comment utiliser ces prédicats de manière élégante.

Tester si une expression est évaluée à VRAI

Imaginons qu'on dispose d'un prédicat contient_le_mot() qui permet de tester si le string chaine envoyé contient un mot qu'on recherche.

1 2
def contient_le_mot(chaine, mot): return mot in chaine

Un exemple d'utilisation :

>>> contient_le_mot("Je veux me plaindre de mes notes bla bla", "plaindre") True

On pourrait ainsi lancer un traitement automatique de certaines requêtes reçues en fonction de mots clés contenus dans des emails (par exemple le mot "montre" ou "viagra" ou "héritage"...) Voici comment faire :

1 2 3 4
if contient_le_mot(chaine, "montre"): instruction1 : mettre un sigle SPAM sur l'email instruction2 : placer l'email dans le dossier SPAM ...

Nous aurions pu écrire ceci mais c'est plus long et inutile si vous avez compris qu'on réalise le bloc d'instructions décalées uniquement si l'expression derrière le if est évaluée à True :

1 2 3 4
if contient_le_mot(chaine, "montre") == True: instruction1 : mettre un sigle SPAM sur l'email instruction2 : placer l'email dans le dossier SPAM ...

Et en plus, vous demandez à l'interpréteur d'utiliser un opérateur supplémentaire (==) alors qu'il ne sert à rien. Imaginez la perte de temps sur un programme de très grande taille si à chaque évaluation nous rajoutions quelque chose de ce type.

12-A° Imaginons qu'on veuille tester si une moyenne est suffisante pour valider un bloc d'enseignement à l'université. Créons pour cela la fonction estValide().

1 2 3
def estValide(note): '''Renvoie True si la note est supérieure ou égale à 10''' return note >= 10

Les deux utilisations ci-dessous sont-elles valides ? L'une des deux versions elle-est à priviligier ?

Version A

1 2
if estValide(15): print(Semestre validé. Passage en deuxième année)

Version B

1 2
if estValide(15) == True: print(Semestre validé. Passage en deuxième année)

...CORRECTION...

Par défaut, Python évalue la condition et réalise les actions du bloc si la condtion est évaluée à True. Mais comme ici nous obtenons directement un booléen, pas la peine d'en rajouter : le cas A est suffisant et clair.

Ok
if estValide(15):

Vous auriez pu taper ceci également, mais c'est inutile :

Ok mais bof bof
if estValide(15) == True:

Voyons maintenant, l'inverse : comment réaliser une action uniquement si on vous répond FAUX.

Tester si une expression est évaluée à FAUX

On dispose du prédicat contient_le_mot() qui permet de savoir si un mot est contenu dans une autre chaîne de caractères.

Par contre, on veut agir uniquement lorsque le mot n'est pas présent.

On pourrait donc produite une instruction conditionnelle if-elif comme ci-dessous (mais assez moche) :

  • if contient_le_mot() est évaluée à True : le mot est présent et on exécute l'instruction pass, qui sert à ne rien faire justement. On passe.
  • elif contient_le_mot() est évaluée à False : le mot n'est pas présent et on réalise les instructions de ce bloc.

Comme vous le voyez, c'est plutôt moche de devoir dire de ne rien faire en utilisant pass et de donner ensuite le cas où on veut agir.

On peut faire mieux en agissant directement si le mot n'est pas présent en tapant ceci :

1 2 3 4
if contient_le_mot(chaine, "montre") == False: instruction1 instruction2 ...

On va bien réaliser les deux instructions uniquement si l'email ne contient pas le mot "montre".

Ou même :

1 2 3 4
if contient_le_mot(chaine, "montre") != True: instruction1 instruction2 ...

Cette fois, l'utilisation de l'opérateur d'égalité == ou de différence != ne semble pas superflue.

Et bien, on peut faire plus court en utilisant le mot-clé not : ce mot-clé inverse la valeur du booléen situé derrière lui.

>>> condition = False >>> condition False >>> not condition True

Du coup, on peut écrire plus proprement le fait d'agir uniquement si le mot n'apparaît pas :

1 2 3 4
if not contient_le_mot(chaine, "montre"): instruction1 instruction2 ...

12-B° Imaginons qu'on veuille convoquer à un oral de rattrapage les étudiants ayant raté leur semestre. Nous voulons toujours utiliser la fonction estValide().

1 2 3
def estValide(note): '''Renvoie True si la note est supérieure ou égale à 10''' return note >= 10

Les deux utilisations ci-dessous sont-elles valides ? L'une des deux versions elle-est à priviligier ?

Version A

1 2
if not estValide(15): print(Semestre raté. Convocation à l'oral)

Version B

1 2
if estValide(15) == False: print(Semestre raté. Convocation à l'oral)

...CORRECTION...

Comme la fonction renvoie un booléen, nous pourrions faire ceci :

Ok mais bof bof
if estValide(15) == False:
Ok mais bof bof
if estValide(15) != True:

Mais en fait, il suffit d'inverser la réponse de la fonction avec not

Ok et élégant
if not estValide(15):

12-C° Quelle va être la ligne interprétée (L2 ou L4) si comparer() renvoie False sur cet appel.

1 2 3 4
if not comparer(choix, m): tache_1() else: tache_2()

...CORRECTION...

not comparer(choix, m)

not False

True

Comme la condition de la ligne 1 donne True, on effectue la fonction tache_1().

5 - ET / OU

Imaginons qu'on veuille créer une fonction est_valide() qui vérifie qu'une note est bien comprise entre 0 et 20.

1 2 3 4 5 6 7 8 9 10 11 12
def est_valide(n): '''Fonction qui renvoie True si n est un entier dans [0;20] :: param n(int) :: un entier quelconque :: return (bool) :: True si n est dans [0;20], False sinon ''' if n > 20: return False elif n < 0: return False return True

13° Expliquer pourquoi la fonction est_valide() ne renvoie pas systématiquement True alors qu'il s'agit de la dernière ligne de cette fonction.

...CORRECTION...

Tout simplement car on sort de la fonction dès qu'on rencontre un return.

Si l'un des deux tests précédents renvoie True, on va donc exécuter le return False et sortir de la fonction.

✎ 14° Un élève propose la fonction ci-dessous. Expliquer clairement pourquoi sa fonction n'est pas correcte alors qu'elle ne déclenche pas d'erreur et qu'elle renvoie bien True pour une note n de 15 par exemple.

1 2 3 4 5 6 7 8 9 10 11 12
def est_valide(n): '''Fonction qui renvoie True si n est un entier dans [0;20] :: param n(int) :: un entier quelconque :: return (bool) :: True si n est dans [0;20], False sinon ''' if n <= 20: return True elif n >= 0: return True return False

Si vous ne trouvez pas, demandez-vous ce qu'elle renvoie pour une note de -15.

Pour obtenir un code plus lisible et concis, il existe un moyen d'associer la réponse de plusieurs tests logiques.

Voyons comment cela fonctionne.

15° Tapez les instructions ci-dessous dans la console.

>>> note = 15 >>> note >= 0 True >>> note < 0 False >>> note <=20 True >>> note > 20 False >>> note >= 0 and note <= 20 True >>> note = 25 >>> note >= 0 and note <= 20 False

Question : que doivent valoir la condition a et la condition b pour que l'expression booléenne suivante soit évaluée à True ?

a and b

...CORRECTION...

L'expression n'est évaluée à True qui si les deux conditions sont également évaluées à True. Il s'agit d'un ET logique.

Voici donc une manière plus simple de créer notre fonction est_valide(). Cela ne change en rien son utilisation : pour l'utilisateur, le changement est totalement invisible.

1 2 3 4 5 6 7 8
def est_valide(n): '''Fonction qui renvoie True si n est un entier dans [0;20] :: param n(int) :: un entier quelconque :: return (bool) :: True si n est dans [0;20], False sinon ''' return n >= 0 and n <= 20

Une seule ligne, hors documentation. Pas mal non ?

✎ 16° Un élève propose la fonction ci-dessous. Expliquer clairement pourquoi vous pensez que sa fonction est correcte ou fausse (pensez à regarder les préconditions sur le paramètre n).

1 2 3 4 5 6 7 8
def est_valide(n): '''Fonction qui renvoie True si n est un entier dans [0;20] :: param n(int) :: un entier quelconque :: return (bool) :: True si n est dans [0;20], False sinon ''' return n > -1 and n < 21

Nous allons revoir en détail le ET dans d'autres activités. Voici simplement ce qu'on nomme la table de vérité d'un ET :

Valeur de a Valeur de b a ET b
VRAI VRAI VRAI
FAUX VRAI FAUX
VRAI FAUX FAUX
FAUX FAUX FAUX

Il existe également le OU

Un ET est évalué à VRAI uniquement lorsque toutes les entrées sont VRAIES.
Sinon, il est évalué à FAUX.
Globalement, dès qu'une condition est FAUX, le ET est FAUX.

Le OU est évalué à FAUX uniquement lorsque toutes les entrées sont FAUSSES.
Sinon, il est évalué à VRAI.
Globalement, dès qu'une condition est VRAI, le OU est VRAI.

17° Compléter la table de vérité du OU :

Valeur de a Valeur de b a OU b
VRAI VRAI ?
FAUX VRAI ?
VRAI FAUX ?
FAUX FAUX ?

Cela revient à chercher cela avec Python :

a or b

...CORRECTION...

Valeur de a Valeur de b a OU b
VRAI VRAI VRAI
FAUX VRAI VRAI
VRAI FAUX VRAI
FAUX FAUX FAUX

✎ 18° QCM : On demande à quatre élèves de créer une fonction note_invalide(). Elle doit renvoyer True si la note n n'est pas dans [0;20].

On vous fournit ci-dessous la réponse de leur fonction. Quel élève fournit la bonne façon de répondre ?

  • A : return n >= 0 or n <= 20
  • B : return n <= 0 or n >= 20
  • C : return n > 0 or n < 20
  • D : return n < 0 or n > 20

6 - If sans condition booléenne ?

Si vous êtes ici, vous avez certainement compris qu'avec un IF, on effectue le bloc tabulé du dessous si l'expression booléenne derrière le IF renvoie True.

1 2 3 4
if expression_booléenne_1: fait un truc elif expression_booléenne_2: fait autre chose

Mais que se passe-t-il si on place une expression non booléenne derrière le if ou le elif ?

Un cas assez typique en informatique : on veut effectuer une action sur une donnée uniquement si elle est vraiment "remplie". Imaginez que vous vouliez extraire le caractère suivant dans une chaîne de caractères. Autant vérifier avant l'extraction qu'il y ai bien quelque chose à extraire, non ?

Voyons comment on peut faire cela.

Convertir en booléen

La fonction native bool() permet de convertir n'importe quelle expression en booléen. Voyons comment cette fonction a été programmée pour répondre Vrai ou Faux en fonction de ce qu'on transmet.

Dans les faits, avec Python :

  • 0 ou 0.0 ou "" (un string vide) ou None (la réponse d'une fonction qui n'a rien à dire en réponse) ou [] (un tableau vide) sont évalués à False lorsqu'on tente de les convertir en booléen
  • >>> bool(0) False >>> bool(0.0) False >>> bool(None) False >>> bool("") False >>> bool([]) False
  • le reste sera évalué à True.
  • >>> bool(1) True >>> bool(-10.2) True >>> bool("bonjour") True >>> bool([1]) True

Pourquoi parler de cela ici ? Tout simplement à cause de la nature du if expression_booléenne.

Ce cas est facile à comprendre car vous savez maintenant que l'expression derrière le if va être évaluée à True ou False

  • if a > b:

L'expression a > b est déjà une expression booléenne, il est donc inutile de tenter de la convertir en booléen en utilisant la fonction native bool(), mais on pourrait taper cela :

  • if bool(a > b):

Du coup, cela marche aussi en fournissant juste une variable non booléenne ou la réponse d'une fonction non booléenne.

  • if a:

Python va réagir comme si vous aviez tapé ceci :

  • if bool(a):

Et donc, en fonction du contenu de a, l'instruction conditionnelle va s'exécuter ou pas.

19° Donner les contenus des variables r1, r2, r3, r4 obtenus après exécution du programme ci-dessous.

1 2 3 4 5 6 7 8 9 10
def alors(n): reponse = 10 if n: reponse = 20 return reponse r1 = alors(45) r2 = alors("") r3 = alors("bonjour') r4 = alors([])

...CORRECTION...

r1 : avec le premier envoi, la condition de la ligne 3 est évaluée à True, et la fonction renvoie donc 20.

r2 : avec le premier envoi, la condition de la ligne 3 est évaluée à False car la variable n'est qu'une chaîne de caractères vide, et la fonction renvoie donc 10.

r3 : avec le premier envoi, la condition de la ligne 3 est évaluée à True, et la fonction renvoie donc 20.

r4 : avec le premier envoi, la condition de la ligne 3 est évaluée à False car la variable n'est qu'un tableau vide, et la fonction renvoie donc 10.

Tester le remplissage ou l'existence d'un string ou d'un tableau

On peut tester si un tableau ou un string est vide et agir en conséquence en utilisant une simple instruction conditionnelle.

Exemple si on veut agir de façon différente si le tableau n'est pas vide ou s'il est vide.

1 2 3 4 5
if tab: # si le tableau tab n'est pas vide, tab évalué à True actions_si_le_tableau_n_est_pas_vide else: # sinon, c'est que tab est vide actions_si_le_tableau_est_vide

Ici if tab doit donc être traduit par "Si tab contient quelque chose d'évaluable par VRAI".

On peut maintenant réaliser une fonction qui peut nous aider à gérer les réservations d'un restaurant.

✎ 20-difficile° Donner et expliquer le contenu des variables possible et numero si elle existe.

Le programme commence en ligne 21, le reste étant de la déclaration de fonctions.

21 22 23 24 25 26
reservations = ['Alice', '', 'Bob', '', '', 'Clark'] possible = reste_une_table_vide(reservations) if possible: numero = trouver_une_table(reservations)
  • En L21, on déclare le tableau contenant les réservations de tables.
  • En L23, on teste et stocke le fait qu'une table soit encore libre.
  • En L25-26, si il reste une table libre, on obtient un numéro de table disponible.

Pour répondre, il faudra suivre le déroulé exact du programme, expliquer lors des appels ce que contient tel ou tel paramètre... Tenter de comprendre le programme dans sa globalité, sans suivre réellement son cheminement n'a que peu de chance d'aboutir. Séquentialité.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
# Déclaration des fonctions def reste_une_table_vide(tables): '''Prédicat qui renvoie True s'il reste une table non reservée''' for i in range(len(tables)): # on récupère un à un les indices du tableau if not tables[i]: return True return False def trouver_une_table(tables): '''Renvoie l'indice de la première table non réservée''' for i in range(len(tables)): # on récupère un à un les indices du tableau if not tables[i]: return i # Corps du programme principal reservations = ['Alice', '', 'Bob', '', '', 'Clark'] possible = reste_une_table_vide(reservations) if possible: numero = trouver_une_table(reservations)

7 - FAQ

Du coup, on peut tester le type des variables ?

Oui. Les deux manières de faire les plus courantes sont fournies ci-dessous :

Première méthode : tester le type de la variable.

>>> a = 5.0 >>> type(a) == float True >>> type(a) == int False >>> type(a) == str False >>> a = "5.0" >>> type(a) == float False >>> type(a) == int False >>> type(a) == str True

Deuxième façon de faire : utiliser la fonction native isinstance.

>>> a = 5 >>> isinstance(a, float) False >>> isinstance(a, int) True >>> isinstance(a, str) False

Maintenant que vous avez vu ou revu les notions d'instructions conditionnelles et de boucles bornées, voyons comment on peut les utiliser pour réaliser des choses plus concrêtes qu'un simple calcul.

Activité publiée le 01 11 2020
Dernière modification : 01 10 2021
Auteur : ows. h.