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 - Cours

Le cours n'est pas destiné à être lu d'un bloc. Vous pouvez passer si vous travaillez en autonomie : les bouts de cours à connaître sont fournis au fur et à mesure dans les activités.

1 - Booléens

Les ordinateurs n'ont que deux choix possibles lorsqu'on leur donne une proposition à tester (du style "Sommes-nous en 2025 ?"). La réponse est donc 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, l'évaluation de l'expression "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
2 - Instruction ou expression ?

Une instruction va modifier l'état de la mémoire. L'affectation est l'instruction la plus fondamentale.

Une expression est un ensemble de valeurs et d'opérateurs pouvant être évaluée. L'évaluation d'une expression correspond à la recheche de sa valeur.

Si l'expression ne contient pas d'instruction, il n'y a pas de modification de l'état du programme.

Exemple d'affection utilisant une évaluation à droite :

>>> c = b > a

On peut dire que b > a est 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 puisqu'on ne la redirige nulle part):

>>> "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.

3 - Structure générale des instructions conditionnelles SI - SINON SI - SINON

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

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

Nombre de blocs

  • Un unique bloc SI et il doit toujours être en première position
  • 0, 1 ou autant de blocs SINON SI que vous voulez (il peut ne pas y en avoir)
  • 0 ou 1 bloc SINON et il doit toujours être en dernière position : c'est le bloc par défaut. Si aucun autre n'a été activé, on prend celui-ci.
4 - Déroulement d'une séquence if-elif-else en Python

A SAVOIR IMPERATIVEMENT

L'interpréteur Python n'exécute que l'un des blocs A, B ou C d'une structure if-elif-else.

Il exécute les instructions du bloc de la première expression évaluée à True puis quitte la structure même si les autres expressions sont vraies également.

Exemple

Instructions Avant 1 Instructions Avant 2
if note > 11: # BLOC A Instruction A1 Instruction A2 Instruction A3
elif note < 9: # BLOC B Instruction B1 Instruction B2 Instruction B3
else: # BLOC C Instruction C1 Instruction C2
Instructions Après 1 Instructions Après 2

Déroulement

Bloc A if : SI l'expression A (note > 11) est évaluée à True, il exécute A1-A2-A3 puis atteint directement Après 1. Sinon, il passe au bloc B.

Bloc B elif : SINON, SI l'expression B (note < 9) est évaluée à True, il exécute B1-B2-B3 puis atteint directement Après 1. Sinon, il passe au bloc C.

Bloc C else : on réalise forcément les instructions C1-C2 si on atteint ce bloc car c'est le bloc else qui veut dire SINON, sous entendu "dans tous les autres cas".

5 - Deux façons de gérer les fonctions contenant un SI

RAPPEL : on sort d'une fonction et on fait disparaître toute la mémoire dédiée dès qu'on rencontre un return.

Version propre avec une seule "porte de sortie"

  • AVANTAGE : plus facile de surveiller la correction de la fonction (le fait que la fonction réponde toujours correctement)
  • DESAVANTAGE : il faut stocker la réponse dans une variable et lire ensuite séquentiellement la fonction jusqu'à trouver l'unique sortie.
  • 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éESAVANTAGE : moins facile à surveiller (plusieurs portes de sortie à contrôler).
  • AVANTAGE : on n'a pas besoin en tant que lecteur humain 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 :

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

Exemples

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

  • La fonction trop_gentil() qui renvoie une note augmentée de +2 si la note d'entrée est inférieure à 10 et, sinon, +1 pour une note inférieure à 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 si la note d'entrée est inférieure à 10 et, sinon, +3 dans les autres cas. La fonction ne doit jamais renvoyer de notes supérieures à 20 : on impose L8-L9 cette condition juste avant de renvoyer la réponse par l'unique porte de sortie.
  • 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" : on se retrouve à faire du copier-coller de la vérification des valeurs des sorties (L6-L7 et L12-L13). La correction de la fonction est plus compliquée à faire.
  • 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
6 - 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 ?"

7 - Prédicat

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

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

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

Le prédicat validant le semestre donnerait simplement ceci :

1 2 3
def valider_semestre(note:int) -> bool: '''Prédicat qui renvoie True si la note est supérieure ou égale à 10''' return note >= 10
8 - Tester si une expression est évaluée à VRAI

Imaginons qu'on dispose d'un prédicat contient_le_mot() qui permet de tester si une chaine de caractères contient un mot qu'on recherche.

1 2
def contient_le_mot(chaine:str, mot:str) -> bool: return mot in chaine

Un exemple d'utilisation :

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

Bonne pratique de programmation

On pourrait ainsi lancer un traitement automatique de certains emails reçus 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_a_tester, "montre"): instruction1 : mettre un sigle SPAM sur l'email instruction2 : placer l'email dans le dossier SPAM ...

Mauvaise pratique de programmation

Nous aurions pu écrire ceci mais c'est plus long et inutile puisque le principe est bien de faire le bloc si l'expression 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 ...
9 - Tester si une expression est évaluée à FAUX

Imaginons qu'on veuille agir lorsqu'un mot n'est pas présent dans un message. On dispose d'un prédicat contient_le_mot() qui permet de savoir si un mot est contenu dans une chaîne de caractères.

Très mauvaise pratique de programmation

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

1 2 3 4 5 6
if contient_le_mot(chaine, "montre") == True: pass else: instruction1 instruction2 ...

C'est moche de dire de ne rien faire puis de donner le cas où on veut agir.

Assez mauvaise pratique de programmation

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 ...

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.

Bonne pratique de programmation

On peut faire mieux 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

On peut alors é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 ...
10 - Le ET logique avec Python

Voici la table de vérité d'un ET :

Valeur de a Valeur de b a and b
True True True
False True False
True False False
False False False

A RETENIR : le ET n'est VRAI que lorsque toutes les entrées sont à VRAI.

11 - Le OU logique avec Python

Voici la table de vérité d'un OU :

Valeur de a Valeur de b a or b
True True True
False True True
True False True
False False False

A RETENIR : le OU n'est FAUX que si toutes les entrées sont FAUSSES.

12 - Convertir en booléen

La fonction native bool() permet de convertir n'importe quelle expression en booléen. Avec Python :

  • Tout ce qui peut s'évaluer à zéro, vide ou inexistant donne False :
    • 0,
    • 0.0,
    • "" (un string vide),
    • None (la réponse d'une fonction qui n'a rien à dire en réponse),
    • [] (un tableau vide),
    • () (un tuple vide),
    • {} (un dictionnaire vide)
    >>> 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
13 - Instruction conditionnelle sans expression booléenne

En Python, on peut fournir une expression non booléenne derrière un if ou un elif : l'interpréteur Pyhon va alors cherhcer à l'interpréter en booléen de lui-même.

Si vous tapez if a:

Python va réagir comme si vous aviez tapé if bool(a):

Si a n'est pas vide ou 0, on effectura le bloc.

14 - Tester le remplissage 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.

Voici comment 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".

2 - Booléens et opérateurs booléens

1 - Booléens

Les ordinateurs n'ont que deux choix possibles lorsqu'on leur donne une proposition à tester (du style "Sommes-nous en 2025 ?"). La réponse est donc 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.

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
2 - Instruction ou expression ?

Une instruction va modifier l'état de la mémoire. L'affectation est l'instruction la plus fondamentale.

Une expression est un ensemble de valeurs et d'opérateurs pouvant être évaluée. L'évaluation d'une expression correspond à la recheche de sa valeur.

Si l'expression ne contient pas d'instruction, il n'y a pas de modification de l'état du programme.

Exemple d'affection utilisant une évaluation à droite :

>>> c = b > a

On peut dire que b > a est 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 puisqu'on ne la redirige nulle part):

>>> "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.

3 - Instruction conditionnelle SI

3 - Structure générale des instructions conditionnelles SI - SINON SI - SINON

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

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

Nombre de blocs

  • Un unique bloc SI et il doit toujours être en première position
  • 0, 1 ou autant de blocs SINON SI que vous voulez (il peut ne pas y en avoir)
  • 0 ou 1 bloc SINON et il doit toujours être en dernière position : c'est le bloc par défaut. Si aucun autre n'a été activé, on prend celui-ci.
4 - Déroulement d'une séquence if-elif-else en Python

A SAVOIR IMPERATIVEMENT

L'interpréteur Python n'exécute que l'un des blocs A, B ou C d'une structure if-elif-else.

Il exécute les instructions du bloc de la première expression évaluée à True puis quitte la structure même si les autres expressions sont vraies également.

Exemple

Instructions Avant 1 Instructions Avant 2
if note > 11: # BLOC A Instruction A1 Instruction A2 Instruction A3
elif note < 9: # BLOC B Instruction B1 Instruction B2 Instruction B3
else: # BLOC C Instruction C1 Instruction C2
Instructions Après 1 Instructions Après 2

Déroulement

Bloc A if : SI l'expression A (note > 11) est évaluée à True, il exécute A1-A2-A3 puis atteint directement Après 1. Sinon, il passe au bloc B.

Bloc B elif : SINON, SI l'expression B (note < 9) est évaluée à True, il exécute B1-B2-B3 puis atteint directement Après 1. Sinon, il passe au bloc C.

Bloc C else : on réalise forcément les instructions C1-C2 si on atteint ce bloc car c'est le bloc else qui veut dire SINON, sous entendu "dans tous les autres cas".

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:int) -> str: '''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 : si le paramètre reponse contient 1987, comment est évaluée l'expression reponse > annee_creation_python  ? Va-t-on alors activer la ligne 5 ?
  3. Ligne 6 : si le paramètre reponse contient 1987, comment est évaluée l'expression reponse < annee_creation_python ? Va-t-on activer la ligne 7 ?
  4. Quel est mot-clé correspondant à SI en Python ?
  5. Quel est mot-clé correspondant à SINON SI en Python ?
  6. Quel est mot-clé correspondant à 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 à un des blocs d'une instruction conditionnelle.

On retrouve la syntaxe des déclarations de fonctions avec def et des boucles bornées avec for.

C'est l'indentation à 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.

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

...CORRECTION...

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

Dès qu'il rencontre une expression évaluée VRAIE, il exécute les instructions du bloc correspondant puis quitte la structure conditionnelle SANS tester les possibilités restantes.

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.

Version 2 valide (plusieurs sorties)

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"

AVANTAGE : cette structure permet au lecteur humain de savoir immédiatement la réponse qui va être donner dans ce cas.

DESAVANTAGE : il va être plus difficile de vérifier et surveiller les réponses de cette fonction. C'est comme surveiller un batiment : c'est plus facile de contrôler les sorties s'il n'y a qu'une seule sortie.

Version 3 valide (plusieurs sorties, pas de else final)

Une autre personne pense qu'on peut encore faire plus rapide en supprimant le else final avec cette version 03. Cette version 03 est valide également puisque si on arrive en ligne 12, c'est que les autres return n'ont pas été rencontrés.

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"

AVANTAGE : on gagne une ligne. Bof.

Version 4 incorrecte (une sortie, pas de else final)

Un enthousiaste pense du coup à modifier la première version et propose une version 04 (incorrecte attention).

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.

5 - Deux façons de gérer les fonctions contenant un SI

RAPPEL : on sort d'une fonction et on fait disparaître toute la mémoire dédiée dès qu'on rencontre un return.

Version propre avec une seule "porte de sortie"

  • AVANTAGE : plus facile de surveiller la correction de la fonction (le fait que la fonction réponde toujours correctement)
  • DESAVANTAGE : il faut stocker la réponse dans une variable et lire ensuite séquentiellement la fonction jusqu'à trouver l'unique sortie.
  • 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éESAVANTAGE : moins facile à surveiller (plusieurs portes de sortie à contrôler).
  • AVANTAGE : on n'a pas besoin en tant que lecteur humain 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 :

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

Exemples

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

  • La fonction trop_gentil() qui renvoie une note augmentée de +2 si la note d'entrée est inférieure à 10 et, sinon, +1 pour une note inférieure à 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 si la note d'entrée est inférieure à 10 et, sinon, +3 dans les autres cas. La fonction ne doit jamais renvoyer de notes supérieures à 20 : on impose L8-L9 cette condition juste avant de renvoyer la réponse par l'unique porte de sortie.
  • 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" : on se retrouve à faire du copier-coller de la vérification des valeurs des sorties (L6-L7 et L12-L13). La correction de la fonction est plus compliquée à faire.
  • 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 10 11 12 13 14 15
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 .. exemples .. >>> traduction('green') 'vert' >>> traduction('grine') 'traduction inconnue pour cette couleur' ''' couleur = "traduction inconnue pour cette couleur" return couleur

4 - 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.

6 - 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 ?"

7 - Prédicat

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

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

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

Le prédicat validant le semestre donnerait simplement ceci :

1 2 3
def valider_semestre(note:int) -> bool: '''Prédicat qui renvoie True si la note est supérieure ou égale à 10''' return note >= 10

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

1 2 3
def valider_semestre(note:int) -> bool: '''Prédicat qui renvoie True si la note est supérieure ou égale à 10''' return note >= 10

...CORRECTION...

note >= 10

7 >= 10

False

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

1 2 3 4 5 6
def valider_semestre(note:int) -> bool: '''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.

11° Compléter la fonction comparer() pour qu'elle renvoie True si les deux paramètres sont égaux. Aucune condition de type sur les deux paramètres.

1 2 3
def comparer(proposition, mystere) -> bool: '''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) -> bool: '''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) -> bool: '''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) -> bool: '''Renvoie True si les deux paramètres sont égaux, False sinon''' return proposition == mystere

5 - Inversion d'un booléen avec not

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

8 - Tester si une expression est évaluée à VRAI

Imaginons qu'on dispose d'un prédicat contient_le_mot() qui permet de tester si une chaine de caractères contient un mot qu'on recherche.

1 2
def contient_le_mot(chaine:str, mot:str) -> bool: return mot in chaine

Un exemple d'utilisation :

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

Bonne pratique de programmation

On pourrait ainsi lancer un traitement automatique de certains emails reçus 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_a_tester, "montre"): instruction1 : mettre un sigle SPAM sur l'email instruction2 : placer l'email dans le dossier SPAM ...

Mauvaise pratique de programmation

Nous aurions pu écrire ceci mais c'est plus long et inutile puisque le principe est bien de faire le bloc si l'expression 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 ...

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:int) -> bool: '''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 à privilégier ?

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.

9 - Tester si une expression est évaluée à FAUX

Imaginons qu'on veuille agir lorsqu'un mot n'est pas présent dans un message. On dispose d'un prédicat contient_le_mot() qui permet de savoir si un mot est contenu dans une chaîne de caractères.

Très mauvaise pratique de programmation

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

1 2 3 4 5 6
if contient_le_mot(chaine, "montre") == True: pass else: instruction1 instruction2 ...

C'est moche de dire de ne rien faire puis de donner le cas où on veut agir.

Assez mauvaise pratique de programmation

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 ...

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.

Bonne pratique de programmation

On peut faire mieux 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

On peut alors é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:int) -> bool: '''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 à privilégier ?

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...

La version A est la plus élégante.

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

Comme la fonction renvoie un booléen, nous pourrions également utiliser la version B mais c'est moins "propre" :

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

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...

La ligne 1 va être progressivement évaluée de cette manière :

if not comparer(choix, m)

if not False

if True

Comme la condition de la ligne 1 donne True, on effectue son bloc et on exécute donc la fonction tache_1().

6 - 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
def est_valide(n:int) -> bool: '''Prédicat qui renvoie True si n est un entier dans [0;20]''' 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
def est_valide(n:int) -> bool: '''Prédicat qui renvoie True si n est un entier dans [0;20]''' 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 a et b pour que l'expression booléenne a and b soit évaluée à True ?

...CORRECTION...

L'expression n'est évaluée à True qui si les deux expressions a b sont 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
def est_valide(n:int) -> bool: '''Fonction qui renvoie True si n est un entier dans [0;20]''' 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
def est_valide(n:int) -> bool: '''Prédicat qui renvoie True si n est un entier dans [0;20]''' return n > -1 and n < 21
10 - Le ET logique avec Python

Voici la table de vérité d'un ET :

Valeur de a Valeur de b a and b
True True True
False True False
True False False
False False False

A RETENIR : le ET n'est VRAI que lorsque toutes les entrées sont à VRAI.

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 or b
True True ?
False True ?
True False ?
False False ?

Cela revient à chercher cela avec Python :

a or b

...CORRECTION...

Valeur de a Valeur de b a or b
True True True
False True True
True False True
False False False

✎ 18° QCM : On demande à quatre élèves de créer une fonction-prédicat 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
11 - Le OU logique avec Python

Voici la table de vérité d'un OU :

Valeur de a Valeur de b a or b
True True True
False True True
True False True
False False False

A RETENIR : le OU n'est FAUX que si toutes les entrées sont FAUSSES.

OU LOGIQUE différent que OU courant

Attention, dans l'utilisation courante d'un OU dans une discussion, on indique souvent que c'est une chose ou alors une autre qui est possible, jamais les deux. Fromage ou dessert. Il pleut ou pas. Le ou du langage courant correspond à ce qu'on appelle en logique un OU exclusif : c'est l'un ou l'autre, pas les deux.

Si vous dites 'Fromage ou dessert' à un informaticien, il prendra les deux ! Oui, le OU logique est bien évaluée à VRAI si les deux sont VRAIS.

7 - If sans condition booléenne ?

Avec un IF, on exécute un bloc tabulé si l'expression booléenne est 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 ?

12 - Convertir en booléen

La fonction native bool() permet de convertir n'importe quelle expression en booléen. Avec Python :

  • Tout ce qui peut s'évaluer à zéro, vide ou inexistant donne False :
    • 0,
    • 0.0,
    • "" (un string vide),
    • None (la réponse d'une fonction qui n'a rien à dire en réponse),
    • [] (un tableau vide),
    • () (un tuple vide),
    • {} (un dictionnaire vide)
    >>> 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):
13 - Instruction conditionnelle sans expression booléenne

En Python, on peut fournir une expression non booléenne derrière un if ou un elif : l'interpréteur Pyhon va alors cherhcer à l'interpréter en booléen de lui-même.

Si vous tapez if a:

Python va réagir comme si vous aviez tapé if bool(a):

Si a n'est pas vide ou 0, on effectura le bloc.

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.

14 - Tester le remplissage 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.

Voici comment 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.

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

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

21 22 23 24
reservations = ['Alice', '', 'Bob', '', '', 'Clark'] if reste_une_table_vide(reservations): numero = trouver_une_table(reservations)
  • En L21, on déclare le tableau contenant les réservations de tables.
  • En L23, on teste le fait qu'une table soit encore libre.
  • En L24, on récupère 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é.

Demandez de l'aide s'il le faut, ce n'est pas facile.

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 : 04 10 2022
Auteur : ows. h.