II. Bonnes pratiques de programmation¶
Cours¶
A. Spécification des fonctions¶
A.1. Prototypage¶
On définit est_majeur(age)
qui prend en paramètre un entier age
et renvoie un booléen valant True
si age
est supérieur à 18
et False
sinon.
Pour avoir les informations sur le type des entrées et des sorties, on ajoute l'information de la manière suivante :
Code
def est_majeur(age : int) -> bool:
majeur = age >= 18
return majeur
Cela correspond à la signature de la fonction, que l'on peut aussi écrire sous la forme int -> bool
.
A.2. Préconditions et postconditions¶
Dire qu'age
doit être un entier ne suffit pas à décrire les valeurs que la variable peut prendre. En effet, un age négatif n'a pas de sens... alors que le type int
en Python peut stocker un entier négatif comme un entier positif. Il faut donc rajouter la condition que age
doit être positif (ou nul).
Une condition que doit respecter une variable d'entrée est une précondition, et une condition que doit respecter une variable de sortie est une postcondition.
Indiquer le(s) précondition(s) et postcondition(s) de l'exemple sur le schéma ci-dessous :
graph LR
age --> F[est_majeur];
F --> majeur
A.3. Documentation¶
La documentation d'une fonction, aussi appelée docstring (documentation écrite avec une chaîne de caractères), précise ce qu'elle doit faire. Elle rajoute des informations par rapport à son nom (qui doit déjà être bien choisi), et peut contenir le type des variables si la signature de la fonction n'est pas écrite.
Exemple
Code
def est_majeur(age):
""" Renvoie True si l'entier age est supérieur à 18 et False sinon
"""
majeur = age >= 18
return majeur
B. Utilisation de modules¶
Une bibliothèque ou module est un ensemble de fonctions déjà écrites, et regroupées ensemble car elle servent pour même un domaine d'application.
Exemples
La bibliothèque random
contient des fonctions permettant de simuler l'aléatoire, turtle
des fonctions permettant de manipuler une tortue pour dessiner, matplotlib
sert à visualiser des données.
Il y a différentes manières de faire un import des fonctions dans votre programme Python, en voici 2 à retenir :
- from bibliotheque import fonction1, fonction2
: importe seulement fonction1
et fonction2
de la bibliothèque,
- from bibliotheque import *
: importe toutes les fonctions de la bibliothèque. Attention ! si vous importez plusieurs bibliothèques et qu'elles ont des fonctions du même nom, celles du premier import
seront écrasées.
C. Structure d'un programme¶
Les différents éléments d'un programme doivent conserver la structure suivante :
Code
# --------- Import des modules utilisés ---------
from bibli1 import fonction1, fonction2
# --------- Définition des fonctions ---------
def ma_fonction1():
...
def ma_fonction2(param):
...
# --------- Programme principal ---------
# Déclaration des variables
a = 1
b = "bonjour"
# Test des fonctions
assert ma_fonction1() == "quelque chose"
assert ma_fonction2(b) == 25
Il faut d'abord définir ce dont on a besoin (import des modules et définition des fonctions), avant de l'utiliser (programme principal).
D. Test de programmes¶
Tester un programme revient à :
- tester chacune de ses fonctions,
- tester le fonctionnement global du programme.
Pour tester une fonction, on définit plusieurs cas qui correspondent à différents comportements de la fonction. Par exemple, si on a une fonction avec plusieurs conditions comme la suivante :
Code
def nb_jours_debut_annee(mois):
if mois == 2:
nb_jours = 28
elif mois%2 == 0:
nb_jours = 30
else:
nb_jours = 31
return nb_jours
On teste alors ce qu'il se passe pour les cas où :
- mois
vaut 2
,
- mois
est pair, par exemple vaut 6
,
- mois
est impair, par exemple vaut 3
.
Les tests peuvent se faire :
- dans la console,
- avec des assertions lorsque la fonction renvoie une valeur, dans le programme principal. Ils seront alors conservés dans le fichier .py
.
En pratique, commencez par faire les tests dans la console pour vérifier que l'on obtient le résultat attendu, puis écrivez-les dans l'éditeur sous la forme d'assertion.
Exemple
Ecrire les appels de fonction à faire dans la console, puis les assertions correspondante à conserver dans le fichier .py
.
TD : Débogage et spécifications¶
A. Déboguer un programme¶
Pour chacun des exemples donnés, indiquer :
- l'erreur affichée dans la console après exécution ou bien le comportement du programme,
- la signification de cette erreur,
- comment corriger ce code pour qu'il fasse ce qui était attendu.
-
Affichage de
"Bonjour, monde"
:Code
print("Bonjour, monde"
-
Calul de la somme des nombres de 1 à 10 compris :
Code
for i in range(1, 11): total = total + i print("La somme est :", total)
-
Affichage de chaque entier de 1 à 10 compris :
Code
i = 1 while i <= 10: print(i)
-
Affichage des carrés des entiers de 1 à 5 :
Code
for i in range(1, 6): print(i ** 2)
-
Définition d'une fonction
multiplier
multipliant deux paramètresa
etb
:Code
def multiplier(a, b): resultat = a * b return resultat x = 7 y = 2 print("Le résultat de la multiplication est :", multiplier())
-
Définition d'une fonction
ajouter_trois
ajoutant3
au paramètrex
:Code
def ajouter_trois(x): resultat = x + 3 nombre = 5 print("Le résultat est :", resultat)
B. Spécifications¶
-
Réécrire la fonction
mystere
du TP "Fonctions et portée d'une variable" en :- changeant son nom, pour en avoir un plus explicite,
- ajoutant sa signature,
- ajoutant sa documentation.
Code
def mystere(a): p = 1 for i in range(a): p = 2*p return p
-
Un élève de NSI veut pouvoir calculer sa moyenne en fonction des notes qu'il a déjà, et de celle de la dernière évaluation. Il a déjà eu 11.5, 15 et 14, de coefficients 1, 2 et 4. La prochaine évaluation aura un coefficient 4. Il veut prévoir sa moyenne en fonction de sa dernière note, et écrit une fonction
moyenne(note)
prenant en paramètre cette dernière note.a. Donner le prototypage de sa fonction.
b. Décrire les préconditions sur les paramètres et les postconditions sur le résultat.
c. Ecrire la fonction
moyenne(note)
en ajoutant des assertions pour les préconditions et postconditions, et la signature de la fonction.
TP : Prototypage et tests¶
Créer un fichier TP_Prototypage.py
dans votre dossier NSI
, et dans le dossier seq4
. Ecrire les réponses aux questions sur votre cahier de TP.
Vous êtes développeur Python et on vous demande d'écrire une fonction calculant la racine carrée d'un nombre. On demande que chaque fonction soit documentée de la manière suivante (il n'est pour l'instant pas demandé de remplir le tableau) :
Etape | Question | Réponse |
---|---|---|
1 | Quel nom la fonction peut-elle avoir ? | |
2 | Combien de paramètres sont acceptés en entrée ? De quel type ? | |
3 | Quels sont les valeurs acceptées en entrée ? | |
4 | Combien de valeurs sont renvoyées en sortie ? Préciser le type. | |
5 | Quels sont les valeurs en sortie possibles ? |
N.B.: La racine carrée d'un nombre est toujours positive ou nulle et n'est calculée que pour un nombre qui est aussi positif ou nul.
-
Les informations demandées permettent de prototyper la fonction. En déduire ce que veut dire prototyper une fonction.
Plus précisément :
- L'étape 3 décrit les préconditions de la fonction : les conditions que doivent vérifier ses entrées,
- L'étape 5 décrit les postconditions de la fonction : les conditions que doivent vérifier les sorties.
-
Compléter le tableau.
-
On donne le code suivant, implémentant la méthode de Héron pour trouver la racine carrée d'un nombre :
Code
def racine(x): y = 1 for i in range(10): y = (y+x/y)/2 return y
Réécrire ce code dans l'éditeur et le compléter avec sa spécification sous la forme suivante (remplacer type_de_a par le type de a, type_de_x par le type de x, et la partie
"Rôle de la fonction"
par ce qu'elle fait) :Code
def racine(x : type_de_x) -> type_de_y: ''' Rôle de la fonction ''' y = 1 for i in range(10): y = (y+x/y)/2 return y
-
Après avoir exécuté votre code, consulter sa documentation en tapant dans la console la commande :
help(racine)
-
Faites l'appel de
racine(-1)
dans la console. Que se passe-t-il ? Est-ce que le comportement de la fonction est clair pour l'utilisateur ? -
On ne peut pas toujours faire confiance à l'utilisateur pour bien utiliser un code. Nous allons le forcer à respecter le type de l'entrée, avec une assertion. La syntaxe est la suivante :
Compléter votre fonction en ajoutant l'assertion avant les premières instructions de la fonction :assert expression booléenne testant ce qui nous intéresse, "Message que l'on veut voir apparaître lorsque le test renvoie False"
Code
def racine(x: ...)-> ...: """ Garder la documentation de la question précédente """ assert x >= 0 , "à compléter avec un message indiquant à l'utilisateur quel est le problème" ...
Exécuter le code. Que se passe-t-il maintenant lorsque vous appelez la fonction (dans la console) avec l'argument
-1
? -
Les assertions peuvent aussi être utilisées pour vérifier que l'on obtient le bon résultat en sortie d'une fonction. On teste donc si un appel de fonction donne le résultat attendu. Ajouter, dans le programme principal (donc en dehors de la définition de
racine
) :Code
assert round(racine(4)) == 2, "Erreur pour la valeur 4"
a. Exécuter le programme, et vérifier que rien ne se passe (cela veut dire que le test fait dans l'assertion renvoie
True
).
b. La fonctionround
permet d'arrondir le résultat, pour qu'il soit un entier. Pourquoi fait-on cette opérations ici (faire le lien avec le cours sur la représentation des nombres) ?
c. Rajouter 2 autres assertions, avec des tests différents, pour vérifier le bon fonctionnement de votre fonction.
TP : Bibliothèque random
et stockage dans des fichiers¶
Créer un fichier TP_Bibliotheque_Fichiers.py
dans votre dossier NSI
, et dans le dossier seq4
. Ecrire les réponses aux questions sur votre cahier de TP.
A. Trouver le nombre¶
L'objectif de cette partie est de programmer un jeu dans lequel on demande à l'utilisateur de deviner un nombre, qui est choisi aléatoirement par l'ordinateur. Pour avoir ce nombre aléatoire, on utilise la bibliothèque random
qui sert à simuler l'aléatoire, et particulièrement la fonction randint
.
On veut que le programme :
- stocke dans une variable
nombre_mystere
le nombre à deviner compris entre0
et100
, - demande à l'utilisateur d'entrer un nouveau nombre (avec la fonction
input
) tant qu'il n'a pas trouvé la solution ou qu'il a atteint le maximum de 10 essais, - si l'utilisateur entre un nombre trop grand, l'ordinateur affiche
"Le nombre mystère est inférieur"
, s'il est trop petit, l'ordinateur affiche"Le nombre est supérieur"
, - si l'utilisateur a deviné, le programme s'arrête et il affiche le nombre d'essais de l'utilisateur.
-
Reformuler la description de l'algorithme utilisé en faisant apparaître la structure du programme, et les instructions à exécuter par l'ordinateur ligne après ligne.
-
Importer la fonction avec l'instruction suivante à placer en début de programme
from random import randint
. -
Avec l'instruction
help(randint)
(tapée dans la console), on obtient sa documentation : son prototypage, et ce qu'elle fait.a. Quels sont ses paramètres ?randint(a, b) method of random.Random instance Return random integer in range [a, b], including both end points.
b. Que renvoie-t-elle ? -
Déduire des questions précédentes comment utiliser
randint
pour que l'ordinateur choisisse le nombre mystère. -
Implémenter (traduire en programme) l'algorithme de la question 1. > N'oubliez pas que la valeur renvoyée par
input
est une chaîne de caractères, que vous devez ici convertir en entier avec la fonctionint
. -
Tester le programme en :
- entrant 10 fois un mauvais nombre pour vérifier que le programme s'arrête,
- essayant de bien deviner le nombre et en vérifiant que le nombre de coups affiché correspond au nombre de fois que vous avez proposé un nombre.
B. Stockage du score dans un fichier¶
On veut maintenant sauvegarder le score de l'utilisateur dans un fichier. Pour ouvrir un fichier, on utilise la fonction open
, et pour écrire la fonction write
.
-
open
prend en paramètres un nom de fichier et un mode d'ouverture (et on peut ajouter d'autres paramètres). Avec l'instructionhelp(open)
, trouver à quoi correspondent les différents modes possibles. -
Pour écrire dans un fichier, il faut l'ouvrir puis écrire. Cela s'écrit avec une syntaxe proche de celle du code suivant :
Code
mot = "ce que je veux stocker" with open('fichier.txt','w') as f : f.write(mot)
- Ce code enregistre dans le fichier
fichier.txt
le contenu de la variablemot
. - Le fichier est accessible avec la variable nommée ici
f
. - Pour écrire avec la fonction
write
, on utilise une syntaxe un peu particulière :f.write(...)
Modifier ce code et le placer à la fin de votre programme de manière à stocker le score qui est le nombre de coups qu'a mis l'utilisateur pour trouver le nombre mystère. Le fichier utilisé se nommera
score.txt
. Ce qui est écrit dans le fichier doit être de type chaîne de caractères, vous pouvez faire la conversion avec la fonctionstr()
. - Ce code enregistre dans le fichier
Pour aller plus loin... (à faire si vous avez le temps)
Faire en sorte que lorsque l'utilisateur rejoue une partie, le score stocké dans le fichier ne soit actualisé que si le nouveau score est meilleur.