Outils Conditions

Identification

Infoforall

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

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 :

>>> c = b > a

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

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

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 Instruction SINON SI cette autre expression booléenne est vraie Instruction Instruction SINON (dans tous les autres cas) Instruction Instruction

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(2000) ???

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(2000) 'Python a été créé avant 2000' >>> creer_message(1980) 'Python a été créé après 1980' >>> creer_message(1991) 'Oui, Python est sorti pour la première fois en 1991'

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

✎ 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 - Fonction booléenne

Nous avons déjà vu le test d'égalité. C'est à dire tester si une variable contient bien 5 par exemple.

Pour tester une égalité en Python, on n'utilise pas a = b qui donne un ordre d'affectation : 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 teste réellement si les deux variables sont égales. Elle est équivalente à la question "Les contenues des variables a et b sont-ils identiques ?"

Fonction boolèenne

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

True ou False en Python.

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 avez 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 une fonction booléenne (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 fonctions booléennes de manière élégante.

Tester une évaluation sur VRAI

Imaginons qu'on dispose d'une fonction booléenne estSalleVide() qui permet de tester si une salle est vide.

Globalement, si on veut faire des choses si la salle est bien vide (comme lancer un nettoyage automatique...), il suffit de faire ceci (on considère que la variable salle transmise contiennent les données de la salle):

1 2 3 4
if estSalleVide(salle): instruction1 instruction2 ...

Nous aurions pu écrire ceci mais c'est plus long :

1 2 3 4
if estSalleVide(salle) == True: instruction1 instruction2 ...

Plus long, inutile une fois qu'on a compris qu'on effectue le bloc si l'expression fournie derrière le if est évaluée à True justement). 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.

Imaginons qu'on veuille tester si une moyenne est bien validée avec la fonction estValide().

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

Il suffit d'en faire l'appel de cette façon :

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

Par défaut, Python évalue la condition et valide si la réponse est True. Mais comme ici nous obtenons directement un booléen, pas la peine d'en rajouter.

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

Ok mais bof bof
if estValide(15) == True:
Tester une évaluation sur FAUX

Lorsqu'on possède une fonction booléenne comme estSalleVide() qui permet de savoir si une salle est vide, on dispose aussi d'assez d'informations pour savoir si une salle est occupée :

  • Si estSalleVide() renvoie True, c'est que la salle est vide.
  • Si estSalleVide() renvoie False, c'est que la salle n'est pas vide. C'est donc qu'elle est occupée !

Si on veut agir uniquement sur une salle occupée, on peut alors utiliser ceci :

1 2 3 4
if estSalleVide(salle) == False: instruction1 instruction2 ...

Ou même :

1 2 3 4
if estSalleVide(salle) != True: instruction1 instruction2 ...

Cette fois, l'opérateur == ou != n'est pas superflu.

Et bien, on peut faire plus simple et plus clair 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 la salle est occupée :

1 2 3 4
if not estSalleVide(salle): instruction1 instruction2 ...

Imaginons qu'on veuille tester si une moyenne n'est pas validée avec la fonction estValide().

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° 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 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 condition1 et condition2 pour que l'expression booléenne suivante soit évaluée à True ?

condition1 and condition2

...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 - Tester l'existence ou le remplissage

Un cas assez typique en informatique : on veut effectuer une action sur une donnée uniquement si elle est vraiment remplie.

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

Mais on peut aussi le faire dans l'autre sens :

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

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.

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): '''Fonction booléenne 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 # Programme principal reservations = ['Alice', '', 'Bob', '', '', 'Clark'] possible = reste_une_table_vide(reservations) if possible: numero = trouver_une_table(reservations)

7 - Tkinter

Pour ceux qui ont le temps. L'utilisation d'une interface graphique n'est pas au programme de NSI.

Nous allons finir avec une petite application graphique Tkinter qui permet de réaliser une animation : une balle rebondit sur les parois.

S'agissant d'un dessin qu'on modifie rapidement à l'écran pour donner l'illusion d'un déplacement, le programme crée plusieurs éléments :

  • Ligne 78 : la fenêtre de l'application (de type Tk)
  • 78
    fenetre = creation_interface("Balle balle", "#8844BB")
  • Ligne 81 : un nouveau widget (de type Canvas) qui est simplement une zone dans laquelle nous allons pouvoir dessiner des choses
  • 81
    canvas = tk.Canvas(fenetre, bg="dark grey", height=300, width=300, borderwidth=0, highlightthickness=0)
  • Ligne 88 : un dessin rond dont la variable numero_mobile n'est rien d'autres que le numéro du dessin dans le Canvas précédent. Pour le créer, on doit donner les deux coordonnées x1, y1 du point en haut à gauche et les deux coordonnées x2, y2 du point en bas à droite. Comme on décale de 20 pixels à chaque fois, on obtient un rond et pas une forme ovale.
  • 88
    numero_mobile = canvas.create_oval(xo, yo, xo+20, yo+20, width=1, fill="yellow")

21° Mettre le programme en mémoire pour vérifier qu'il fonctionne à peu près : la balle rebondit bien mais pas sur les parois. Pour activer le déplacement, il faut appuyer sur les flèches gauche ou droite de votre clavier.

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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
# - 1 - importation des modules nécessaires import tkinter as tk import random as rd # - 2 - variables globales DPCT = 3 # - 3 - Déclaration des fonctions def creation_interface(titre, couleur): '''Crée et renvoie la référence d'interface graphique :: param titre(str) :: le titre visible dans la barre de l'application :: param couleur(str) :: une couleur de fond valide pour l'application :: return (tk.Tk) :: la référence Tkinter de l'application ''' fe = tk.Tk() fe.geometry("350x350") fe.title(titre) fe.configure(bg=couleur) return fe def activer_gestion_evenements(): '''!activation des surveillances des flèches gauche et droite''' fenetre.bind('<Right>', lancement_droite) fenetre.bind('<Left>', lancement_gauche) def stopper_gestion_evenements(): '''Annule l'activation des surveillances des flèches gauche et droite''' fenetre.unbind('<Right>') fenetre.unbind('<Left>') def lancement_droite(e): '''Fonction évenementielle qui lance juste droite()''' stopper_gestion_evenements() droite() def lancement_gauche(e): '''Fonction évenementielle qui lance juste gauche()''' stopper_gestion_evenements() gauche() def droite(): '''Fait avancer le mobile de DPCT pixels vers la droite''' canvas.move(numero_mobile, DPCT, 0) canvas.update() if coordonnee_horizontale(numero_mobile, canvas) <= 190: fenetre.after(50, droite) else: fenetre.after(50, gauche) def gauche(): '''Fait avancer le mobile de DPCT pixels vers la gauche''' canvas.move(numero_mobile, -DPCT, 0) if coordonnee_horizontale(numero_mobile, canvas) >= 110: fenetre.after(50, gauche) else: fenetre.after(50, droite) def coordonnee_horizontale(n, c): '''Renvoie l'abscisse du centre du mobile de numéro n dans le canvas c :: param n(int) :: numéro du dessin à étudier :: param c(Canvas) :: référence du Canvas dans lequel le dessin est présent :: return (int) :: la coordonnée x voulue ''' return (c.coords(n)[0] + c.coords(n)[2]) // 2 # - 4 - Programme principal # - 4.1 - Création de la fenêtre du logiciel fenetre = creation_interface("Balle balle", "#8844BB") # - 4.2 - Création du Canvas zone_dessin, la zone de déplacement du mobile canvas = tk.Canvas(fenetre, bg="dark grey", height=300, width=300, borderwidth=0, highlightthickness=0) canvas.place(x=25, y=25) # - 4.3 - Création du mobile qu'on va déplacer sur le Canvas xo = rd.randint(100,200) yo = rd.randint(100,200) numero_mobile = canvas.create_oval(xo, yo, xo+20, yo+20, width=1, fill="yellow") # - 4.4 - Création des événéments activer_gestion_evenements() # - 4.5 - Surveillance de l'application fenetre.mainloop() # Surveillance des événements sur la fenêtre

Questions

  1. Sur quelles lignes sont crées les liaisons entre les événement Flèche Droite et Flèche Gauche aux fonctions évenementielles lancement_gauche() et lancement_droite() ?
  2. A quoi voit-on que les fonctions lancement_gauche() et lancement_droite() sont des fonctions événementielles ?
  3. Que provoque les instructions de lancement_droite() ?

...CORRECTION...

  1. LIGNE 92 : on lance la fonction activer_gestion_evenements() et on arrive LIGNES 28 et 29 :
  2. 26 27 28 29
    def activer_gestion_evenements(): '''!activation des surveillances des flèches gauche et droite''' fenetre.bind('<Right>', lancement_droite) fenetre.bind('<Left>', lancement_gauche)
  3. Ces deux fonctions possèdent un paramètre (nommé ici e) qui sera rempli automatiquement avec les informations sur l'événément qui a provoqué son activation.
  4. Il suffit de lire les instructions de la fonction :
    • Ligne 38 : on active la fonction stopper_gestion_evenements() qui désactive la surveillance des touches gauche et droite avec la méthode unbind() (cela évite de pouvoir "démarrer" plusieurs fois l'animation).
    • Ligne 39 : on lance la fonction droite()

Pour comprendre l'animation, il faut étudier les fonctions gauche() et droite() et savoir ce que réalisent les méthodes qu'on y trouve :

  • la méthode move()
  • la méthode update()
  • la méthode after()
46 47 48 49 50 51 52 53
def droite(): '''Fait avancer le mobile de DPCT pixels vers la droite''' canvas.move(numero_mobile, DPCT, 0) canvas.update() if coordonnee_horizontale(numero_mobile, canvas) <= 190: fenetre.after(50, droite) else: fenetre.after(50, gauche)

Comment cela fonctionne ?

  • Ligne 48 : la méthode move() permet de déplacer l'un des dessins d'un canvas en x et en y. On doit donner le nom du canvas devant la méthode, puis, entre les parenthèses de la méthode, le numéro du dessin, le décalage en x en pixels et le décalage en y en pixels. On voit ici qu'on va donc déplacer le rond de DPCT pixels horizontalement et 0 pixels verticalement.
  • Ligne 49 : la méthode update() impose au canvas de mettre à jour son apparence. Il le fait régulièrement mais on force ici son changement d'apparence pour être certain que la modification soit prise en compte.
  • Ligne 50 : on teste la coordonnée x centrale du rond à l'aide de la fonction coordonnee_horizontale() dont je n'expliquerai pas le fonctionnement ici. Sachez juste qu'elle renvoie cette coordonnée lorsqu'on lui fournit le numéro d'un dessin et la référence du canvas. Si la coordonnée horizontale est inférieure à 190 pixels (on est pas encore trop à droite), on voit qu'on réactive la fonction droite() quelques millisecondes plus tard. Sinon, on active gauche() pour repartir vers la gauche.
  • Lignes 51 et 53 : la méthode after() demande de lancer une fonction dont on fournit l'adresse après un certain nombdre de millisecondes (50 ms ici). Rappel 50 ms = 50.10-3 s = 0.050 s.

22° Expliquer comment fonctionne la fonction gauche().

Finalement, modifier la fonction de façon à ce que la balle fasse bien des aller-retours lorsqu'elle "percute" le bord du canvas.

Les tests conditionnels, les boucles bornées FOR.

Il nous reste à associer ces deux notions et voir la boucle non bornée : la boucle TANT QUE que va réaliser le travail demandé TANT QUE la condition proposée est évaluée à VRAI.

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