Prérequis : cours sur la mise au point des programmes
À l’issue de ce TP, vous saurez :
assert et avec pytest ;time.perf_counter().Les activités pratiques qui suivent ont pour objectif de vous faire appliquer les notions présentées dans le cours :
Une pizzeria souhaiterait créer une sorte de drive automatique pour ses clients afin qu’ils puissent commander en ligne une pizza selon les stocks disponibles.
Un stagiaire a déjà commencé l’écriture d’un programme prototype : pizza_drive.py .
Cependant comme vous allez pouvoir le constater, le programme est largement perfectible :
À l’aide des exceptions, rendez ce programme plus robuste et plus ergonomique pour le client, c’est-à-dire sans plantage inopiné.
Provoquer chacun des trois problèmes et lire le nom de l’exception affichée : des lettres au lieu d’un nombre déclenchent ValueError, la pizza 3 IndexError. Une boucle while True peut alors redemander le choix tant qu’il n’est pas valide : try ... except ValueError autour de la conversion, puis un test sur le numéro et un autre sur le stock. Le squelette ci-dessous remplace la boucle while de la méthode prendre_la_commande.
while True:
try:
self.choix_client = int(input("Votre choix ? "))
except ValueError:
print("Veuillez saisir un nombre.")
continue
if self.choix_client not in (0, 1, 2):
print("Ce numéro n'existe pas.")
elif ...:
print("Cette pizza n'est plus disponible.")
else:
break
Correction publiée après la séance.
Téléchargez et décompressez l’archive bogues.zip .
Pour chacun des scripts :
Exécuter le script et comparer le résultat obtenu avec le résultat attendu. En cas d’erreur, lire le message en partant de la dernière ligne : le type de l’erreur, puis la ligne en cause. Sinon, afficher les valeurs des variables avec print, ou suivre l’exécution pas à pas dans le débogueur de Thonny.
Correction publiée après la séance.
À votre tour de mettre en œuvre les tests unitaires sur une classe Rectangle à réaliser. Vous utiliserez la méthode simple avec le mot-clef assert, puis la bibliothèque pytest dans un second temps.
L’entreprise GEOFORME développe des logiciels et outils de dessin industriel.
Le développeur avant vous dans le service a commencé à développer une classe Rectangle
disponible dans le fichier rectangle.py
. Il a principalement défini l’API de la classe
et positionné des TODO comme ci-dessous dans chaque méthode :
# TODO : compléter le code de la méthode
pass
Si vous détectez des problèmes potentiels, vous pourrez les signaler en levant une
Exception comme ci-dessous :
# Un problème potentiel...
if probleme == True:
raise Exception("Un problème est survenu")
Le responsable du service vous demande de finir de coder cette classe et d’écrire les tests unitaires qui permettront de la valider et de l’intégrer dans le reste du logiciel de dessin industriel.
Concernant les tests unitaires, vous créerez deux versions et donc deux fichiers :
tests_rectangle_assert.py : version avec le mot-clef asserttests_rectangle_pytest.py : version avec la bibliothèque pytestCompléter d’abord le constructeur et les accesseurs, puis chaque méthode de calcul, en écrivant ses tests au fur et à mesure sur des rectangles dont on connaît les résultats de tête. Pour les problèmes potentiels, se demander quelles dimensions n’ont pas de sens pour un rectangle. Avec pytest, chaque test est une fonction dont le nom commence par test_, et la commande python -m pytest tests_rectangle_pytest.py les exécute toutes.
import pytest
from rectangle import Rectangle
def test_aire():
assert Rectangle(5, 3).calculer_aire() == ...
def test_dimension_negative():
with pytest.raises(Exception):
...
Correction publiée après la séance.
Johnny Begood participe en ce moment à un challenge algorithmique sur une plate-forme en ligne. Pour le problème n°10, il doit écrire un programme qui affiche le \(2000\text{-ième}\) nombre premier. Il a écrit un code qui fonctionne parfaitement bien (le résultat est \(17~389\)), mais la plate-forme a fixé une limite de temps d’exécution (< 100 ms) pour valider le challenge et son programme est trop lent.
Téléchargez son code source : probleme_v00.py .
À l’aide de la fonction perf_counter() du module time, mesurez et affichez le temps d’exécution de son programme et plus particulièrement de sa fonction rechercher_nieme_nombre_premier().
time.perf_counter() renvoie un instant en secondes : le relever juste avant et juste après l’appel de la fonction, puis afficher la différence, comme dans l’exemple du cours.
Notez le résultat moyen obtenu sur trois exécutions et confirmez le refus par la plate-forme.
Johnny a entendu dire qu’en Python les boucles for étaient plus performantes que les
boucles while.
Créez un nouveau script probleme_v02.py, copiez-collez le code de la version v00 et modifiez le code de sa fonction etre_premier() pour remplacer la boucle while par une boucle for.
La boucle while fait prendre à facteur les valeurs 2, 3, …, n − 1 : quel range produit ces mêmes valeurs ? Les lignes facteur = 2 et facteur += 1 deviennent alors inutiles.
Chronométrez le temps d’exécution obtenu et notez le résultat moyen sur trois lancements. Comparez le temps entre les versions v00 et v02. Est-ce suffisant ?
Après avoir lu la page Wikipédia consacrée aux nombres premiers, il découvre qu’un nombre N non premier possède toujours un diviseur compris entre 2 et \(\sqrt{N}\) : il suffit donc de tester les facteurs jusqu’à \(\sqrt{N}\).
Créez un nouveau script probleme_v03.py à partir de la précédente version et modifiez la limite de la boucle for de \(n-1\) à \(\sqrt{n}\).
range n’accepte que des entiers : la borne s’écrit int(n ** 0.5) + 1, le + 1 permettant de tester aussi la racine elle-même, comme pour 25 = 5 × 5.
Chronométrez cette nouvelle version comme précédemment et concluez.
Correction publiée après la séance.
Effectuez un profilage du script de départ probleme_v00.py et notez les
résultats :
rechercher_nieme_nombre_premier() :
etre_premier() :
Lancer python -m cProfile probleme_v00.py dans un terminal, depuis le dossier du script. Dans le tableau affiché, ncalls donne le nombre d’appels, percall le temps moyen d’un appel, et cumtime le temps total, y compris celui des fonctions appelées.
Correction publiée après la séance.