Script et programme

Identification

Infoforall

7 - Créer un programme en Python


Pour l'instant, nous en sommes là :

Mon code est pourri but_it_works !
Mon code est pourri ? Oui. Mais il fonctionne !

Aujourd'hui, vous allez justement voir comment structurer un programme.

Evaluation : 6 questions

  questions 05-06-10-13

  questions 03-14

c

Exercices supplémentaires 🏠 : Non

Documents de cours PDF : .PDF

Sources latex : .TEX et entete.tex et licence.tex

Résumé : Version HTML ou fond blanc ou ou PDF (couleur ou gris)

1 - IHM console

Attention aux noms des programmes Python

Vous allez enregistrer votre programme en lui donnant un nom. N'utilisez JAMAIS le nom d'un des modules Python qui existent déjà : turtle, random... Sinon, vous ne pourriez plus utiliser turtle.

Nous allons réaliser un petit jeu : on génère deux entiers au hasard et on demande à l'utilisateur de donner la valeur de leur multiplication. On compte les points et on donne le total à la fin.

1.1 IHM Console

Le disposif qui permet de faire interagir deux systèmes sans que l'un et l'autre ne connaissent les détails de fonctionnement de l'autre se nomme une interface.

Lorsque l'interface se fait entre un homme et une machine, on la nomme IHM : Interface Homme Machine.

La console est l'une des IHM les plus simples puisque l'interaction peut se gérer avec deux fonctions depuis Python.

  1. PROGRAMME vers HUMAIN : print() pour afficher sur l'écran
  2. HUMAIN vers PROGRAMME : input() pour récupérer le texte tapé sur le clavier
1.2 print() pour afficher une variable depuis un programme

1.2.1 Affichage avec print()

La fonction native print() est une fonction d'Interface Homme Machine (IHM) qui affiche un contenu dans la console à destination d'un humain.

>>> a = "Salut à toi !" >>> a 'Salut à toi !' >>> print(a) Salut à toi !

On voit qu'avec print() le texte affiché n'est pas entouré de guillemets. Il s'agit d'un simple texte, un simple affichage.

Cette fonction agit dans le sens MACHINE vers HUMAIN.

1.2.2 Aucune mémoire avec print()

On ne mémorise pas l'information affichée lorsqu'on utilise print().

Exemple explicite : on récupére dans rep la réponse de la fonction print(). rep ne contient rien.

>>> a = "Reçu ?" >>> rep = print("ABC") ABC >>> rep >>>
1.2.3 Exemple de programme

Voici un programme qui affiche le contenu final de c :

1 2 3 4 5 6 7
print("Début du programme") a = 5 b = 10 c = a + b a = 100 # Attention ; ceci ne va pas modifier le c créé sur la ligne précédente print(c) print("Fin du programme")
>>> %Run acti_programme.py Début du programme 15 Fin du programme >>>
1.2.4 Que peut-on envoyer comme entrée à print() ?

Tout ce que vous voulez. C'est l'intérêt de la fonction, elle affiche.

1.2.5 Texte + Variable (plusieurs arguments)

Lorsqu'on veut afficher du texte et des variables, on peut envoyer plusieurs arguments (des strings et des variables) à la fonction, en les séparant par des virgules.

1 2 3 4 5 6 7
print("Début du programme") a = 5 b = 10 c = a + b print("Variable c : ", c) print("Fin du programme")
>>> %Run acti_programme.py Début du programme Variable c : 15 Fin du programme >>>
1.2.6 Texte + Variable (f-string)

Lorsqu'on veut afficher du texte et des variables, on peut plutôt utiliser un f-string. C'est un string :

  • Précédé d'un f avant le symbole d'ouverture.
  • Pouvant contenir des expressions entre accolades : elles seront évaluées et affichées.
1 2 3 4 5 6 7
print("Début du programme") a = 5 b = 10 c = a + b print(f"Variable c : {c}") print("Fin du programme")
>>> %Run acti_programme.py Début du programme Variable c : 15 Fin du programme >>>

⚙ 01° Deux élèves proposent ces deux programmes qui se ressemblent beaucoup.

Le deuxième élève déclare que son programme est meilleur car il est plus court que le précédent et fait la même chose puisqu'il affiche la même chose.

Il se trompe... Expliquez pourquoi le programme 2 ne fait pas la même chose que le programme 1.

Programme 1

1 2 3 4
a = 5 b = 10 c = a + b print(c)

Programme 2

1 2 3
a = 5 b = 10 print(a + b)

...CORRECTION...

Le programme 2 ne fait pas la même chose : il ne mémorise pas le résultat de l'addition.

Il affiche simplement le résultat pour qu'un humain puisse le visualiser. Mais on ne pourra pas continuer à travailler avec ce résultat puisqu'il n'a pas été mémorisé dans une variable.

1.3 Un print() n'est pas un return

SUPER IMPORTANT

Si un énoncé signale : créer une fonction "qui renvoie" "qui calcule" ou mot similaire, il faut utiliser return. On pourra stocker la réponse dans une variable.

Si un énoncé signale : créer une fonction "qui affiche", il faut utiliser print() et il s'agit d'un simple message dans l'IHM, pas de mémorisation.

⚙ 02° Deux fonctions à réaliser :

  • Réaliser une fonction addition() : prototype addition(a:int, b:int) -> int
    • qui attend deux nombres en entrée (on les stockera dans a et b et
    • qui renvoie la somme des deux nombres.
  • Réaliser une fonction affiche() : prototype affiche(a:int, b:int) -> None
    • qui attend deux nombres en entrée (on les stockera dans a et b) et
    • qui affiche la somme des deux nombres.

...CORRECTION...

1 2
def addition(a, b): return a + b

...CORRECTION...

1 2
def affiche(a, b): print(a + b)

Ou si on veut utiliser addition() :

1 2 3 4 5
def addition(a, b): return a + b def affiche(a, b): print(addition(a, b))

2 - Table de multiplication

2.4 print() et caractères de contrôle

Il existe des caractères de contrôle (des caractères qui ne s'affichent pas mais provoquent un effet) : le passage à la ligne, la tabulation...

Pour provoquer l'effet voulu, il faut utiliser print().

2.4.1 Passage à la ligne

Pour passer à la ligne, on utilise \n dans le string ( n pour pour new line).

>>> texte = "Nom\nPrenom\nAge" >>> texte "Nom\nPrenom\nAge" >>> print(texte) Nom Prenom Age

Le passage à la ligne caractèrise un unique caractère, pas 2 :

>>> texte = "\n" >>> len(texte) 1

La fonction native ord() permet de récupérer le code ASCII correspondant.

>>> caractere = "\n" >>> code = ord(caractere) >>> code 10

La fonction native chr() permet de récupérer le caractère correspondant à un code.

>>> code = 10 >>> caractere = chr(code) >>> caractere '\n'
2.4.2 Tabulation

La tabulation \t décale le texte sur une position commune.

>>> a = "Nom\tPrenom\nAlice\tInborderlands\nBob\tLeponge" >>> a 'Nom\tPrenom\nAlice\tInborderlands\nBob\tLeponge' >>> print(a) Nom Prenom Alice Inborderlands Bob Leponge >>> len('\t') 1 >>> ord('\t') 9 >>> chr(9) '\t'
2.4.3 Caractère d'échappement

L'anti-slash est ce qu'on nomme un caractère d'échappement : il signale que le caractère suivant ne doit pas être traité comme un caractère normal : \t veut dire de créer une tabulation, pas d'afficher un anti-slash et un t.

Comment afficher un anti-slash alors ? C'est simple, on place deux anti-slashs à la suite. Le premier est le caractère d'échappement et le suivant indique de cet anti-slash ne doit pas être traité comme un caractère d'échappement mais comme un simple caractère.

>>> print("Voilà un antislash \\") Voilà un antislash \
2.4.4 Caractère Bell

Il s'agit du petit son de cloche signalant souvent un petit problème.

>>> son = chr(7) >>> print(son)

⚙ 03-A° Lancer et comprendre ce programme qui comporte beaucoup de copier-coller.

1 2 3 4 5 6 7 8 9 10 11 12 13
n = 5 print("0 x", n, "\t= ", n*0) print("1 x", n, "\t= ", n*1) print("2 x", n, "\t= ", n*2) print("3 x", n, "\t= ", n*3) print("4 x", n, "\t= ", n*4) print("5 x", n, "\t= ", n*5) print("6 x", n, "\t= ", n*6) print("7 x", n, "\t= ", n*7) print("8 x", n, "\t= ", n*8) print("9 x", n, "\t= ", n*9) print("10 x", n, "\t= ", n*10)
5 x 0 = 0 5 x 1 = 5 5 x 2 = 10 5 x 3 = 15 5 x 4 = 20 5 x 5 = 25 5 x 6 = 30 5 x 7 = 35 5 x 8 = 40 5 x 9 = 45 5 x 10 = 50

Question

Modifier le programme ci-dessous en remplaçant les ... de façon à ce qu'il produise le même affichage. Vous utiliserez bien entendu la variable de boucle valeur.

1 2 3 4
n = 5 for valeur in range(...): print(..., " x ", ..., "\t= ", ...)

...CORRECTION...

1 2 3 4
n = 5 for valeur in range(11): print(n, " x ", valeur, "\t= ", n * valeur)

⚙ 04° Lancer et comprendre ce programme qui comporte des f-strings.

1 2 3 4 5 6 7 8 9 10 11 12 13
n = 5 print(f"0 x {n}\t= {n*0}") print(f"1 x {n}\t= {n*1}") print(f"2 x {n}\t= {n*2}") print(f"3 x {n}\t= {n*3}") print(f"4 x {n}\t= {n*4}") print(f"5 x {n}\t= {n*5}") print(f"6 x {n}\t= {n*6}") print(f"7 x {n}\t= {n*7}") print(f"8 x {n}\t= {n*8}") print(f"9 x {n}\t= {n*9}") print(f"10 x {n}\t= {n*10}")
5 x 0 = 0 5 x 1 = 5 5 x 2 = 10 5 x 3 = 15 5 x 4 = 20 5 x 5 = 25 5 x 6 = 30 5 x 7 = 35 5 x 8 = 40 5 x 9 = 45 5 x 10 = 50

Question

Modifier le programme ci-dessous en remplaçant les ... de façon à ce qu'il produise le même affichage. Vous utiliserez bien entendu des f-strings et la variable de boucle valeur.

1 2 3 4
n = 5 for valeur in range(...): print(...)

...CORRECTION...

1 2 3 4
n = 5 for valeur in range(11): print(f"{n} x {valeur}\t= {n * valeur}")

Passons maintenant à la récupération des données envoyées par l'utilisateur.

[Rappel] La fonction native input()

Principe

La fonction input() permet de récupérer sous forme d'un STRING ce qu'un utilisateur va taper un jour sur le clavier.

L'utilisateur doit valider sa réponse avec la touche ENTREE.

1 2 3 4
print("Donnez votre nom puis validez avec la touche ENTREE : ") reponse = input() print("Merci. Votre nom est :") print(reponse)

Pour comprendre le programme ci-dessus, il vous suffit de savoir que la variable reponse va contenir un string correspondant aux touches enfoncées par l'utilisateur.

  • L2, à droite : on demande à l'utilisateur de fournir son nom;
  • L2, à gauche : on stocke sa réponse dans la variable nommée reponse;
  • L4 : on affichera sa réponse.
Récupérer un entier ?

Réponse simple : on ne peut pas ! Tout ce que vous tapez va être transmis à Python sous forme d'un string.

Si vous tapez 17, input() renvoie un string "17", et pas un int 17.

Mais; on peut utiliser la fonction native int() qui permet de tenter de créer un entier à partir de la donnée qu'on lui fournit. Exemple d'utilisation :

1 2 3 4
print("Donnez votre note puis validez avec la touche ENTREE : ") reponse = int( input() ) print("Comme je suis gentil, je vous rajoute 3 points :") print(reponse + 3)
2.5 Utilisation complète de input()

Utilisation complète

Puisque input() permet de poser une question à l'utilisateur, on peut lui transmettre un string contenant la question voulue.

On peut ainsi taper ceci :
reponse = input("Veuillez fournir un nombre entier : ")

L'interpréteur Python va alloir faire trois choses

  1. Afficher votre message, comme si vous aviez utilisé un print() mais sans passage à la ligne automatique.
  2. Récupérer la réponse de l'utilisateur sous forme d'un string
  3. Placer ce string dans la variable reponse
>>> reponse = int(input("Veuillez fournir une note : ")) Veuillez fournir une note : 5 >>> reponse 5 >>> reponse * 3 15

⚙ 05° Compléter le script à l'aide d'une utilisation complète de la fonction input() de façon à obtenir un affichage proche de celui-ci :

Quelle table voulez-vous ?7 7 x 0 = 0 7 x 1 = 7 7 x 2 = 14 7 x 3 = 21 7 x 4 = 28 7 x 5 = 35 7 x 6 = 42 7 x 7 = 49 7 x 8 = 56 7 x 9 = 63 7 x 10 = 70
1 2 3 4
n = ... for valeur in range(11): print(f"{n} x {valeur}\t= {n * valeur}")

...CORRECTION...

1 2 3 4
n = int( input("Quelle table voulez-vous ?") ) for valeur in range(11): print(f"{n} x {valeur}\t= {n * valeur}")

3 - Jeu de calculs pour les petits

(Rappel) Fonction randint() du module random

A - Fonction randint()

Le module random contient beaucoup de fonctions permettant de gérer l'aléatoire.

Il existe notamment la fonction randint() qui permet de générer un integer aléatoire :
randint(5, 8) génère un nombre aléatoire entre 5 et 8 pouvant valoir 5, 6, 7 ou 8.

Si on veut simuler un d6, un dé à 6 faces, il suffit de taper randint(1, 6) (notez bien qu'on doit d'abord importer la fonction depuis le module avant de pouvoir l'utiliser) :

>>> from random import randint >>> randint(1, 6) 4 >>> randint(1, 6) 1 >>> randint(1, 6) 6 >>> randint(1, 6) 2
B - Exemple d'un programme créant des notes au hasard

Ci-dessous, un programme générant 10 notes aléatoires entre 0 et 20 (notez bien qu'ici, nous importons uniquement le module random) :

1 2 3 4 5 6 7
import random # Importation du module notes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] # Tableau de dix entiers for i in range(10): # Pour i variant de 0 à 9 notes[i] = random.randint(0, 20) # Case i contient un nbr entre 0 et 20 print(notes) # Affiche lorsque la boucle est finie
>>> %Run portee.py [19, 14, 5, 7, 3, 18, 16, 1, 8, 7] >>> %Run portee.py [4, 7, 13, 13, 11, 16, 4, 7, 9, 12]
C - Remarque importante

Notez bien :

  • qu'avec randint(2, 5), la valeur finale est incluse dans les choix possibles : 2, 3, 4 ou 5.
  • qu'avec range(5), la valeur "finale" 5 n'est pas incluse : nous allons générer progressivement 0, 1, 2, 3, 4 mais PAS 5.

A vous de le savoir, cela a été défini comme cela.

⚙ 06° Compléter le script pour tirer au hasard deux nombres a b entre 1 et 10, stocker la réponse de leur multiplication dans la variable m, poser la question à l'utilisateur et incrémenter le compteur bonnes_reponses si la réponse est bonne.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
from random import randint bonnes_reponses = 0 mauvaises_reponses = 0 a = ... b = ... m = ... question = f"Que vaut la multiplication {a} x {b} ? " reponse = ... if ... == ...: bonnes_reponses = ... print("Bravo !") else: mauvaises_reponses = ... print("Raté !")

...CORRECTION...

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
from random import randint bonnes_reponses = 0 mauvaises_reponses = 0 a = randint(1, 10) b = randint(1, 10) m = a * b question = f"Que vaut la multiplication {a} x {b} ? " reponse = int( input(question) ) if reponse == m: bonnes_reponses = bonnes_reponses + 1 print("Bravo !") else: mauvaises_reponses = mauvaises_reponses + 1 print("Raté !")

⚙ 07° Modifions un peu les choses : on veut maintenant réaliser cela (tirage des valeurs, question à l'utilsateur...) dix fois de suite.

Compléter la ligne 6 du programme.

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
from random import randint bonnes_reponses = 0 mauvaises_reponses = 0 for ... a = randint(1, 10) b = randint(1, 10) m = a * b question = f"Que vaut la multiplication {a} x {b} ? " reponse = int( input(question) ) if reponse == m: bonnes_reponses = bonnes_reponses + 1 print("Bravo !") else: mauvaises_reponses = mauvaises_reponses + 1 print("Raté !") print("\nBILAN") print("-----") print(f"Nombre de bonnes réponses : {bonnes_reponses}") print(f"Nombre de mauvaises réponses : {mauvaises_reponses}")

...CORRECTION...

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
from random import randint bonnes_reponses = 0 mauvaises_reponses = 0 for _ in range(10): a = randint(1, 10) b = randint(1, 10) m = a * b question = f"Que vaut la multiplication {a} x {b} ? " reponse = int( input(question) ) if reponse == m: bonnes_reponses = bonnes_reponses + 1 print("Bravo !") else: mauvaises_reponses = mauvaises_reponses + 1 print("Raté !") print("\nBILAN") print("-----") print(f"Nombre de bonnes réponses : {bonnes_reponses}") print(f"Nombre de mauvaises réponses : {mauvaises_reponses}")

⚙ 08° Désormais, nous voulons reposer encore et encore la même question tant que l'utilisateur ne donne pas la bonne réponse. Compléter ce programme où nous avons insérer un TANT QUE et donc modifier la structure.

Compléter les lignes 15, 16 et 20 pour rendre le programme fonctionnel.

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
from random import randint bonnes_reponses = 0 mauvaises_reponses = 0 for _ in range(10): a = randint(1, 10) b = randint(1, 10) m = a * b question = f"Que vaut la multiplication {a} x {b} ? " reponse = int( input(question) ) while ... != ...: mauvaises_reponses = ... print("Raté !") reponse = int( input(question) ) bonnes_reponses = ... print("Bravo !") print("\nBILAN") print("-----") print(f"Nombre de bonnes réponses : {bonnes_reponses}") print(f"Nombre de mauvaises réponses : {mauvaises_reponses}")

...CORRECTION...

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
from random import randint bonnes_reponses = 0 mauvaises_reponses = 0 for _ in range(10): a = randint(1, 10) b = randint(1, 10) m = a * b question = f"Que vaut la multiplication {a} x {b} ? " reponse = int( input(question) ) while reponse != m: mauvaises_reponses = mauvaises_reponses + 1 print("Raté !") reponse = int( input(question) ) bonnes_reponses = bonnes_reponses + 1 print("Bravo !") print("\nBILAN") print("-----") print(f"Nombre de bonnes réponses : {bonnes_reponses}") print(f"Nombre de mauvaises réponses : {mauvaises_reponses}")

⚙ 09° Puisqu'on ne sort du TANT QUE qu'avec la bonne réponse, il est inutile de compter les bonnes réponses !

On décide donc de créer une fonction qui réalise l'étape des demandes de réponses et renvoie le nombre de tentatives pour trouver la bonne réponse : 1 si on a bon tout de suite, plus si on s'est trompé au moins une fois.

  1. Visualiser les lignes 18 à 28 du programme principal pour comprendre comment fonctionne le programme puis
  2. Compléter la fonction pour qu'elle fasse ce qu'on lui demande de faire.
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
from random import randint def questionner(question:str, bonne_reponse:int) -> int: """Renvoie le nombre de fois où on pose la question avant réussite""" tentatives = 1 reponse = ... while ... != ...: tentatives = tentatives + 1 print("Raté !") reponse = ... return tentatives total_tentatives = 0 for _ in range(10): a = randint(1, 10) b = randint(1, 10) m = a * b question = f"Que vaut la multiplication {a} x {b} ? " total_tentatives = total_tentatives + questionner(question, m) print("Bravo !") print("\nBILAN") print("-----") print(f"Nombre de tentatives pour 10 calculs : {total_tentatives}")

...CORRECTION...

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
from random import randint def questionner(question:str, bonne_reponse:int) -> int: """Renvoie le nombre de fois où on pose la question avant réussite""" tentatives = 1 reponse = int(input(question)) while reponse != bonne_reponse: tentatives = tentatives + 1 print("Raté !") reponse = int(input(question)) return tentatives total_tentatives = 0 for _ in range(10): a = randint(1, 10) b = randint(1, 10) m = a * b question = f"Que vaut la multiplication {a} x {b} ? " total_tentatives = total_tentatives + questionner(question, m) print("Bravo !") print("\nBILAN") print("-----") print(f"Nombre de tentatives pour 10 calculs : {total_tentatives}")

✔ 10°Nouvelle amélioration : on rajoute une mesure du temps à l'aide de la fonction time() du module time.

Observer bien :

  • Qu'on regroupe toutes les importations en début du programme;
  • Qu'on mesure le temps sur la ligne 20 avant de commencer le jeu;
  • Qu'on fait de même en ligne 32 après avoir fini le jeu.
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
from random import randint from time import time def questionner(question:str, bonne_reponse:int) -> int: """Renvoie le nombre de fois où on pose la question avant réussite""" tentatives = 1 reponse = int(input(question)) while reponse != bonne_reponse: tentatives = tentatives + 1 print("Raté !") reponse = int(input(question)) return tentatives total_tentatives = 0 temps_depart = time() for _ in range(10): a = randint(1, 10) b = randint(1, 10) m = a * b question = f"Que vaut la multiplication {a} x {b} ? " total_tentatives = total_tentatives + questionner(question, m) print("Bravo !") temps_final = time() temps_epreuve = temps_final - temps_depart print("\nBILAN") print("-----") print(f"Nombre de tentatives pour 10 calculs : {total_tentatives}") print(f"Durée des 10 calculs : {temps_epreuve}")
Hors Programme  : et en cas d'impossibilité de conversion ?

Cela ne tombera pas le jour de l'examen ou dans un DS, mais c'est pratique et nous allons l'utiliser aujourd'hui. Rien à retenir, il suffit de voir un peu commment cela fonctionne.

Il est possible que le string récupéré ne soit pas interprétable en tant qu'entier. Dans ce cas, cela lève une exception :

>>> reponse = int(input("Veuillez fournir une note : ")) Veuillez fournir une note : cinq ValueError: invalid literal for int() with base 10: 'trois'

Le seul moyen de palier à ceci est d'utiliser quelques notions hors programme en NSI. Il s'agit de try except : si Python lève une exception pendant le bloc try, plutôt que d'interrompre brutalement le programme, il saute simplement dans le bloc except. On peut alors y gérer le problème.

1 2 3 4 5 6 7 8 9
def recuperer_une_note(): reponse = "" # "Fausse réponse" pour rentre dans le while while type(reponse) != int: # TQ reponse n'est pas un entier rep_clavier = input("Note : ") # récupère le choix de l'utilisateur try: # Essaye de faire ceci : reponse = int(rep_clavier) # transforme rep_clavier en int except: # Si cela déclenche une Exception pass # on ne fait rien, on passe... return reponse

Voici une utilisation possible en mode interactif (une fois la fonction mise en mémoire bien entendu)

>>> %Run portee.py >>> note = recuperer_une_note() Note : cinq Note : 5 >>> note 5

⚙ 11° Plutôt que d'utiliser un simple input(), nous utiliserons maintenant la fonction recuperer_un_entier() qui repose et repose la question tant qu'elle n'obtient pas un entier.

A faire

  • Observer bien que les utilisations à int(input()) sont simplement remplacées par des appels à recuperer_un_entier().
  • Lire le code de recuperer_un_entier() ligne par ligne jusqu'à comprendre comment elle fonctionne.
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
from random import randint from time import time def questionner(question:str, bonne_reponse:int) -> int: """Renvoie le nombre de fois où on pose la question avant réussite""" tentatives = 1 reponse = recuperer_un_entier(question) while reponse != bonne_reponse: tentatives = tentatives + 1 print("Raté !") reponse = recuperer_un_entier(question) return tentatives def recuperer_un_entier(question:str): reponse = "" # "Fausse réponse" pour rentre dans le while while type(reponse) != int: # TQ reponse n'est pas un entier rep_clavier = input(question) # récupère le choix de l'utilisateur try: # Essaye de faire ceci : reponse = int(rep_clavier) # transforme rep_clavier en int except: # Si cela déclenche une Exception pass # on ne fait rien, on passe... return reponse total_tentatives = 0 temps_depart = time() for _ in range(10): a = randint(1, 10) b = randint(1, 10) m = a * b question = f"Que vaut la multiplication {a} x {b} ? " total_tentatives = total_tentatives + questionner(question, m) print("Bravo !") temps_final = time() temps_epreuve = temps_final - temps_depart print("\nBILAN") print("-----") print(f"Nombre de tentatives pour 10 calculs : {total_tentatives}") print(f"Durée des 10 calculs : {temps_epreuve}")

⚙ 12° Notez (sans justification particulière) pour chacune des variables surlignées dans le programme ci-dessous s'il s'agit d'une variable locale ou d'une variable globale. Il ne s'agit pas d'analyser le code en détail, juste de faire attention aux déclarations.

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
from random import randint from time import time def questionner(question:str, bonne_reponse:int) -> int: """Renvoie le nombre de fois où on pose la question avant réussite""" tentatives = 1 reponse = recuperer_un_entier(question) while reponse != bonne_reponse: tentatives = tentatives + 1 print("Raté !") reponse = recuperer_un_entier(question) return tentatives def recuperer_un_entier(question:str): reponse = "" # "Fausse réponse" pour rentre dans le while while type(reponse) != int: # TQ reponse n'est pas un entier rep_clavier = input(question) # récupère le choix de l'utilisateur try: # Essaye de faire ceci : reponse = int(rep_clavier) # transforme rep_clavier en int except: # Si cela déclenche une Exception pass # on ne fait rien, on passe... return reponse total_tentatives = 0 temps_depart = time() for _ in range(10): a = randint(1, 10) b = randint(1, 10) m = a * b question = f"Que vaut la multiplication {a} x {b} ? " total_tentatives = total_tentatives + questionner(question, m) print("Bravo !") temps_final = time() temps_epreuve = temps_final - temps_depart print("\nBILAN") print("-----") print(f"Nombre de tentatives pour 10 calculs : {total_tentatives}") print(f"Durée des 10 calculs : {temps_epreuve}")

...CORRECTION...

1 2 3 4 5 LOC 6 7 8 LOC 9 10 11 12 13 14 15 16 17 18 LOC 19 20 21 LOC 22 23 24 25 26 27 28 29 30 GLO 31 GLO 32 33 34 35 36 37 38 39 GLO 40 41 42 43 44 45 46 47 48
from random import randint from time import time def questionner(question:str, bonne_reponse:int) -> int: """Renvoie le nombre de fois où on pose la question avant réussite""" tentatives = 1 reponse = recuperer_un_entier(question) while reponse != bonne_reponse: tentatives = tentatives + 1 print("Raté !") reponse = recuperer_un_entier(question) return tentatives def recuperer_un_entier(question:str): reponse = "" # "Fausse réponse" pour rentre dans le while while type(reponse) != int: # TQ reponse n'est pas un entier rep_clavier = input(question) # récupère le choix de l'utilisateur try: # Essaye de faire ceci : reponse = int(rep_clavier) # transforme rep_clavier en int except: # Si cela déclenche une Exception pass # on ne fait rien, on passe... return reponse total_tentatives = 0 temps_depart = time() for _ in range(10): a = randint(1, 10) b = randint(1, 10) m = a * b question = f"Que vaut la multiplication {a} x {b} ? " total_tentatives = total_tentatives + questionner(question, m) print("Bravo !") temps_final = time() temps_epreuve = temps_final - temps_depart print("\nBILAN") print("-----") print(f"Nombre de tentatives pour 10 calculs : {total_tentatives}") print(f"Durée des 10 calculs : {temps_epreuve}")

⚙ 13° Expliquez pourquoi renommer la variable total_tentatives en tentatives ne poserait aucun problème à Python alors qu'il existe déjà une variable nommée tentatives dans l'une des fonctions.

...CORRECTION...

L'une serait une variable globale et l'autre une variable locale qui ne sera accessible que pour cette fonction.

4 - Mot de passe

⚙ 14° Dans le script ci-dessous, la variable mdp_recu contient le mot de passe reçu et la variable mdp_reel contient le vrai mot de passe attendu. Pour cela, le plus simple est de montrer dans quel ordre l'interpréteur va exécuter cette ligne.

Questions :

  1. Sur quelle ligne se trouve l'instruction qui permet de récupérer l'entrée clavier ?
  2. quel est le type de l'expression mdp_recu == mdp_reel ?
  3. expliquer ce que contiendrait la variable autorisation si l'utilisateur avait tapé 1234 au clavier.
1 2 3 4
print("Entrez votre mot de passe :") mdp_recu = input() mdp_reel = "12345" autorisation = mdp_recu == mdp_reel

...CORRECTION...

  1. Sur quelle ligne se trouve l'instruction qui permet de récupérer l'entrée clavier ?
  2. Il s'agit de la ligne 2 avec input()

  3. quel est le type de l'expression mdp_recu == mdp_reel ?
  4. On utilise l'opérateur == qui permet de comparer le contenu des deux données transmises à gauche et à droite. Il va renvoyer un booléen.

  5. expliquer ce que contiendrait la variable autorisation si l'utilisateur avait tapé 1234 au clavier.
  6. Lorsqu'on arrive en ligne 4, on demande à Python de comparer "1234" et "12345". Les deux étant différents, Python renvoie False et le stocke dans la variable autorisation.

✎ 15° Compléter maintenant ce programme qui demande à l'utilisateur son mot de passe tant que celui qu'il fournit n'est pas le bon. Il faudra vous assurer qu'on rentre bien une première fois dans le tant que, en fournissant une fausse valeur qui ne peut pas être le mot de passe réel.

1 2 3 4 5 6
mdp_reel = "12345" mdp_recu = ... while ... != ...: print("Entrez votre mot de passe :") mdp_recu = input() print("Mot de passe accepté")

✎ 16° Dans le script ci-dessous, le dictionnaire les_mdp contient les bonnes associations nom d'utilisateur - mot de passe et la variable mdp_recu contiendra le mot de passe tapé par l'utilisateur. Lancer le programme pour voir ce qu'il fait puis répondre aux questions.

1 2 3 4 5 6 7 8 9 10 11 12
les_mdp = {"Alice": "12345", "Bob": "0000"} # Commentaire ? nom = "" # Fausse valeur initiale mdp_recu = "" # Fausse valeur initiale while nom not in les_mdp.keys(): # Signification en français ? nom = input("Nom : ") while mdp_recu != les_mdp[nom]: # Signification en français ? mdp_recu = input("Mot de passe : ") print(f"Autorisation fournie à {nom} :") # Commentaire ?

Questions :

  1. L1 : Quelle est la valeur associée à la clé "Alice" dans le dictionnaire les_mdp ?
  2. L6 : Que signifie en français l'expression nom not in les_mdp.keys() ?
  3. L9 : Que signifie en français l'expression mdp_recu != les_mdp[nom] ?

5 - Convertisseur Décimal vers Binaire

Regardons maintenant comment réaliser un convertisseur nombre en décimal vers binaire.

Si vous n'avez jamais vu le binaire, ce n'est pas grave. Avec ce programme, vous allez comprendre pourquoi avec 8 bits (8 cases ne pouvant contenir que 0 ou 1) on ne peut pas coder de nombres entiers supérieurs à 255.

⚙ 17° Complément de connaissances avant de créer le programme de conversion. Tapez ceci dans une console Python.

>>> s = "Bonjour" >>> a = s[1:] >>> a 'onjour' >>> b = s[2:] >>> b 'njour' >>> c = s[3:] >>> c 'jour' >>> s 'bonjour'

Questions 

  1. A quoi sert visiblement de rajouter [1:], [2:], ou [3:] derrière un string ?
  2. A quoi voit-on que cela renvoie un nouveau string et non pas que cela modifie le string de base ?
  3. Les strings sont-ils muables ou immuables dans Python ?

...CORRECTION...

  1. Visiblement [1:] renvoie une copie du string mais en ne gardant que les caractères d'indice 1 et plus. On supprime donc l'ancien caractère 0.
  2. De même, [2:] renvoie une copie du string mais en ne gardant que les caractères d'indice 2 et plus. On supprime donc les caractères 0 et 1.

    De même, [3:] renvoie une copie du string mais en ne gardant que les caractères d'indice 3 et plus. On supprime donc les caractères 0, 1 et 2.

  3. On voit qu'il ne s'agit pas d'une modification du string de base puisque sur la dernière ligne, la demande de visualisation de s montre que le string n'a pas été modifié.
  4. C'est normal puisque les strings sont immuables en Python.

⚙ 18° Complément de connaissances pour le programme de conversion. Tapez ceci dans une console Python : on utilise la fonction native bin() qui permet d'obtenir une conversion d'un entier en binaire (un système où les seuls chiffres disponibles sont 0 ou 1).

>>> bin(64) '0b1000000' >>> bin(65) '0b1000001' >>> bin(66) '0b1000010' >>> bin(200) '0b11001000' >>> bin(200)[2:] '11001000'

Questions 

  1. Quel est le type de la réponse de bin() ?
  2. Quelle indication dans la réponse de bin() permet de comprendre qu'on représente du binaire ?
  3. Que doit-on rajouter pour obtenir uniquement les caractères d'indice 2 et plus ?

...CORRECTION...

    1. On voit que la fonction renvoie un string.
    2. On remarque qu'il y a à chaque fois '0b' en début de string.
    3. Pour supprimer ces deux caractères, il suffit donc de taper bin(200)[2:] par exemple.
    >>> bin(64) '0b1000000' >>> bin(64)[2:] '1000000' >>> bin(200) '0b11001000' >>> bin(200)[2:] '11001000'

✎ 19° Placer ce programme en mémoire. Faire quelques essais pour comprendre par exemple comment on encode 0, 1, 2, 4, 5 et 255 avec un octet (8 bits).

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
# CONSTANTES NBR_BITS = 8 # Nombre de bits minimum affichés # Déclaration des fonctions def message_ouverture(): print("CONVERTISSEUR décimal en binaire") print("Entrer un nombre négatif si vous voulez sortir du convertisseur") def message_final(): print("FIN") def affichage_binaire(n:int): string_binaire = bin(n)[2:] # Commentaire ? if len(string_binaire) < NBR_BITS: # Commentaire ? nb_bits_manquants = NBR_BITS - len(string_binaire) # Commentaire ? string_binaire = "0" * nb_bits_manquants + string_binaire # Commentaire ? print(f"{'-' * 29} └────── {string_binaire}") # Commentaire ? def recuperer_un_entier(question:str): reponse = "" # "Fausse réponse" pour rentre dans le while while type(reponse) != int: # TQ reponse n'est pas un entier rep_clavier = input(question) # récupère le choix de l'utilisateur try: # Essaye de faire ceci : reponse = int(rep_clavier) # transforme rep_clavier en int except: # Si cela déclenche une Exception pass # on ne fait rien, on passe... return reponse # Programme principal nbr = 0 # Commentaire ? message_ouverture() # Commentaire ? while nbr >= 0: # Commentaire ? nbr = recuperer_un_entier("Entier à convertir en binaire : ") if nbr >= 0: # Commentaire ? affichage_binaire(nbr) # Commentaire ? message_final() # Commentaire ?

Questions :

  1. Pourquoi on ne peut-on pas coder plus que 255 avec un seul octet (8 bits) ? Il suffit d'utiliser le programme et de voir ses réponses pour parvenir à répondre.
  2. Quelles sont les quatre fonctions déclarées dans ce programme ?
  3. Que contient string_binaire (type et contenu) après exécution de la ligne 15 sur un appel comme affichage_binaire(5) ?
  4. A quoi servent les lignes 16 à 18 ? Sous quelle condition va-t-on réaliser cette action ?
  5. Commenter les lignes du programme principal et de la fonction affichage_binaire().

...CORRECTION...

  1. 255 ?
  2. >>> %Run convertisseur.py CONVERTISSEUR décimal en binaire Entrer un nombre négatif si vous voulez sortir du convertisseur Entier à convertir en binaire : 0 ----------------------------- └────── 00000000 Entier à convertir en binaire : 1 ----------------------------- └────── 00000001 Entier à convertir en binaire : 2 ----------------------------- └────── 00000010 Entier à convertir en binaire : 3 ----------------------------- └────── 00000011 Entier à convertir en binaire : 254 ----------------------------- └────── 11111110 Entier à convertir en binaire : 255 ----------------------------- └────── 11111111 Entier à convertir en binaire : 256 ----------------------------- └────── 100000000 >>>

    La réponse de 255 montre bien que les 8 bits sont déjà tous à 1 : si on veut rajouter un 1, il faut donc rajouter un bit supplémentaire. Il faut donc 2 octets permettant d'avoir jusqu'à 16 bits.

⚙ 20° En réalité, un formatage beaucoup plus précis est tout à fait possible en utilisant la syntaxe Python plutôt que de chercher nous même le nombre de zéro à rajouter devant nos nombres binaires.

Tapez les commandes ci-dessous dans une console Python puis réecrire la fonction affichage_binaire() pour qu'elle réalise le même travail mais avec un code interne utilisant ce que nous venons de présenter ici.

>>> valeur = 45 >>> bin(valeur) '0b101101' >>> bin(valeur)[2:] '101101' >>> f"La valeur est : {bin(valeur)[2:]}" 'La valeur est : 101101' >>> f"La valeur est : {bin(valeur)[2:]:>8}" 'La valeur est : 101101' >>> f"La valeur est : {bin(valeur)[2:]:>16}" 'La valeur est : 101101' >>> f"La valeur est : {bin(valeur)[2:]:0>16}" 'La valeur est : 0000000000101101' >>> f"La valeur est : {bin(valeur)[2:]:->16}" 'La valeur est : ----------101101' >>> f"La valeur est : {bin(valeur)[2:]:.>8}" 'La valeur est : ..101101'

Vous avez vu comment réaliser des interfaces textuelles avec les deux fonctions print() input(). Dans les activités suivantes, vous allez découvrir comment faire de même avec des interfaces graphiques.

6 - Cours : structure et exécution d'un programme

Cette dernière partie est destinée à être commentée par l'enseignant au tableau plutôt que d'être réalisée en autonomie.

6.1 Différence entre console interactive et programme

Cas de la console

Lorsqu'une ligne contient uniquement une expression, l'interpréteur l'évalue et affiche le résultat.

>>> a = 10 >>> b = a + 2 >>> b 12

On visualise bien que 12 s'affiche sur la console lorsqu'on demande juste d'évaluer b.

Cas du programme

Lorsqu'une ligne contient uniquement une expression, l'interpréteur l'évalue et... c'est tout. Pas d'affichage.

1 2 3
a = 10 b = a + 2 b

Ce programme n'affiche rien alors que la ligne 3 ne contient que la demande d'évaluation d'une expresssion.

Dans un programme, si vous voulez voir quelque chose s'afficher, il faut le demander explicitement :

1 2 3
a = 10 b = a + 2 print(b)
6.2 COMMENTAIRES : expliquer le fonctionnement

Les commentaires sont destinés à un développeur humain et visent à fournir des explications sur le fonctionnement des parties les plus difficiles du code interne : un bon commentaire est un commentaire utile, il doit permettre de comprendre et modifier un code même plusieurs années après sa création initiale.

Pour placer un commentaire en Python, on utilise le caractère dièse (#). Trois exemples à comprendre :

  • Commentaire sur toute une ligne (ligne 1 ci-dessous)
  • 1
    # Toute cette ligne est un commentaire.
  • Commentaire en fin de ligne (ligne 2 ci-dessous)
  • 2
    print("Bonjour tout le monde") # Ceci est également un commentaire
  • La ligne 3 ne comporte aucun commentaire puisque le # fait juste partie d'un string.
  • 3
    print("Cette ligne ne contient pas de # commentaire")

Voici le résultat de ce programme L1 à L3 dans la console : il n'affiche pas les commentaires.

1 2 3
# Toute cette ligne est un commentaire. print("Bonjour tout le monde") # Ceci est également un commentaire print("Cette ligne ne contient pas de # commentaire")
>>> %Run progcommentaires.py Bonjour tout le monde Cette ligne ne contient pas de # commentaire >>>

L'interpréteur Python ne tentera pas d'exécuter les commentaires.

6.3 Structure d'un programme

Voici la structure attendue d'un programme Python :

  1. Importation des modules nécessaires au programme.
  2. Déclarations des CONSTANTES : par convention, le nom des CONSTANTES est constitué uniquement de majuscules.
  3. Déclaration des variables globales destinées à être lues depuis des fonctions : à utiliser avec modération. Elles sont sources de nombreux disfonctionnements lorsqu'elles sont mal gérées.
  4. Déclaration des fonctions.
  5. Les instructions du programme en lui-même : on nomme cette partie "programme principal" parfois. On y place également les variables globales qu'on envoie simplement en tant que paramètres.

Convention

Habituellement, on sépare les parties ci-dessous par au moins deux lignes vides.

6.4 Noms des variables globales et locales

Deux utilisations des variables globales :

A - Lues directement par les fonctions

Cela semble pratique mais si vous décidez de changer le nom dans le programme, c'est l'ensemble des lignes de vos fonctions qu'il va falloir modifier. C'est donc une source d'erreurs.

Exemple avec une fonction qui déplace le crayon pour le déplacer à la position voulue :

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
# Importation des modules supplémentaires import turtle as trt # CONSTANTES et variables globales lues par les fonctions feutre = trt.Turtle() # feutre va être lue par les fonctions # Déclaration des fonctions def deplacer(x, y): feutre.penup() # On lève la pointe feutre.goto(x, y) # On déplace le crayon feutre.pendown() # On abaisse la pointe # Programme feutre.color("red") feutre.fillcolor("orange") feutre.pensize(4) feutre.speed(5) feutre.forward(150) deplacer(0, 100) feutre.forward(150) deplacer(0, -100) feutre.forward(150)
B - Transmises aux fonctions lors de l'appel

Avantage : le nom de la variable globale n'a aucune importance depus la fonction puisqu'on la stocke temporairement dans une variable locale. Changer le nom de la variable d'un côté ou de l'autre n'aura donc aucune incidence.

Désavantage : le code semble plus lourd.

D'ailleurs, on peut même garder le même nom si on veut, Python ne sera pas perdu entre variable locale et globale.

Pour ne pas perdre le lecteur humain, le mieux est d'avoir

  • un nom pour la variable globale (feutre) et
  • un nom proche dans toutes les fonctions qui vont le récupérer en entrée (ftr).
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
# Importation des modules supplémentaires import turtle as trt # CONSTANTES et variables globales lues par les fonctions # Déclaration des fonctions def deplacer(ftr, x, y): ftr.penup() # On lève la pointe ftr.goto(x, y) # On déplace le crayon ftr.pendown() # On abaisse la pointe # Programme feutre = trt.Turtle() # transmise en entrée aux fonctions feutre.color("red") feutre.fillcolor("orange") feutre.pensize(4) feutre.speed(5) feutre.forward(150) deplacer(feutre, 0, 100) feutre.forward(150) deplacer(feutre, 0, -100) feutre.forward(150)

Avant de finir cette activité, quelques rappels sur la façon de comprendre pourquoi l'interpréteur n'est pas content parfois :

Bug 01° Voici un programme qui provoque une erreur. Attention, contrairement à notre habitude en NSI, j'ai importé directement TOUT ce que contient le module turtle en utilisant le caractère magique * qui signifie "tout". C'est une mauvaise pratique en informatique car la plupart du temps, nous n'importons pas qu'un seul module à la fois. Par contre, l'avantage vient du fait que les programmes sont plus courts puisqu'on n'a pas besoin de préfixer chaque fonction par le nom du module qui le contient.

Questions

  • Que signifie le message d'erreur ?
  • A quoi est-ce dû ?
1 2 3 4 5 6 7 8 9 10
write("Origine", font=('Arial', 28, 'normal')) goto(0, 100) write("y > 0", font=('Arial', 28, 'normal')) from turtle import * goto(0, -100) write("y < 0", font=('Arial', 28, 'normal')) goto(300, 0) write("x > 0", font=('Arial', 28, 'normal')) goto(-300, 0) write("x < 0", font=('Arial', 28, 'normal'))
Traceback (most recent call last): File "/home/rv/axes.py", line 1, in <module>: write("Origine", font=('Arial', 28, 'normal')) NameError: name 'write' is not defined

...CORRECTION...

L'interpréteur signale qu'il y a un problème en ligne 1 : in ne connait pas le nom write.

C'est normal puisque ce nom de fonction est inclu dans le module turtle qui n'est importé qu'à partir de la ligne 4.

Bug 02° Ce programme provoque une erreur. L'interpréteur ne comprend pas ce qu'on lui demande.

  1. Sur quelle ligne se situe le problème ?
  2. Quel est le problème ?
1 2 3 4 5 6 7 8 9 10
from turtle import * write("Origine", font=('Arial', 28, 'normal')) goto(0, 100) write("y > 0", font=('Arial', 28, 'normal')) goto(0, -100) write("y < 0", font=('Arial', 28, 'normal')) goto(300, 0) write(x > 0, font=('Arial', 28, 'normal')) goto(-300, 0) write("x < 0", font=('Arial', 28, 'normal'))

...CORRECTION...

Traceback (most recent call last): File "/home/rv/axes.py", line 8, in <module>: write(x > 0, font=('Arial', 28, 'normal')) NameError: name 'x' is not defined

Le problème vient du fait qu'on doit transmettre un string et qu'on a "oublié" de placer les guillemets autour du texte à afficher.

Remarquez bien qu'il a exécuté le programme sur les lignes 1 à 7. Il stoppe à la 8.

Bug 03° Ce programme provoque une erreur. L'interpréteur ne comprend pas ce qu'on lui demande.

  1. Sur quelle ligne se situe le problème ?
  2. Quel est le problème ?
1 2 3 4 5 6 7 8 9 10
from turtle import * write("Origine", font=('Arial', 28, 'normal')) goto(0, 100} write("y > 0", font=('Arial', 28, 'normal')) goto(0, -100) write("y < 0", font=('Arial', 28, 'normal')) goto(300, 0) write("x > 0", font=('Arial', 28, 'normal')) goto(-300, 0) write("x < 0", font=('Arial', 28, 'normal'))

...CORRECTION...

Traceback (most recent call last): File "/home/rv/axes.py", line 3, in <module>: goto(0, 100} ^ SyntaxError: closing parenthesis '}' does not match opening parenthesis '('

Le problème vient du fait qu'on veut fermer les informations transmises à la fonction avec un accolade. Or, on doit utiliser une parenthèse. L'interpréteur Python signale donc qu'il ne comprend rien à ce qu'on désire. Par contre, il a exécuté le programme sur les lignes 1 et 2.

Moralité : avant d'appeler l'enseignant à l'aide parce que Python n'est pas content, commencez par lire le message d'erreur pour localiser le problème et tentez de voir sa nature. C'est ce que je fais quand je viens vous aider !

7 - FAQ

Rien pour le moment

Maintenant que nous avons vu comment créer un programme, nous allons compléter les notions de fonctions, de boucles et d'instructions conditionnelles dans le but de réaliser des programmes de plus en plus complexes.

Activité publiée le 28 08 2019
Dernière modification : 28 11 2024
Auteur : ows. h.