Aller au contenu

03 Mise au point des scripts et gestion des exceptions

Table des matières

  1. Les bonnes pratiques : documenter les fonctions
  2. Les tests
  3. Les préconditions et les postconditions
  4. Les bibliothèques ou modules
  5. Gestion des exceptions
  6. Exercices

1. Les bonnes pratiques : documenter les fonctions

⚓︎

1.1. Qu’est-ce qu’une docstring ?

⚓︎

Une docstring est un texte placé juste après l’en-tête d’une fonction en Python. Elle sert à expliquer :

  1. Ce que fait la fonction ;

  2. Comment l’utiliser (informations sur les paramètres et le résultat attendu) ;

  3. Les conditions d’utilisation pour éviter les erreurs.

Elle est essentielle pour rendre le code plus lisible et plus compréhensible.

1.2. Comment écrire une docstring ?

⚓︎

Activité n°1 : Ajouter une docstring

Tester :

🐍 Script Python
def factorielle(n):
    """
    Fonction qui retourne la factorielle d'un nombre.
    :param n: int
    :return: int
    CU (conditions d'utilisation) : n >= 0
    >>> factorielle(0)
    1
    >>> factorielle(4)
    24
    """
    resultat = 1
    for i in range(2, n + 1):
        resultat = resultat * i
    return resultat

Exécuter :

🐍 Script Python
help(factorielle)
Python

###

Solution

Résultat :

📋 Texte
Help on function factorielle in module __main__:

factorielle(n)
    Fonction qui retourne la factorielle d'un nombre.
    :param n: int
    :return: int
    CU (conditions d'utilisation) : n >= 0
    >>> factorielle(0)
    1
    >>> factorielle(4)
    24

Explication :

  • La docstring est affichée avec la commande help(factorielle).

  • Elle décrit clairement l’objectif et les conditions d’utilisation.

2. Les tests

⚓︎

Les tests permettent de vérifier qu’un programme ne produit pas d’erreur et qu’il effectue bien la tâche attendue.

2.1. Les tests simples avec assert

⚓︎

L’instruction assert est utilisée pour vérifier rapidement que le programme fonctionne correctement.

Activité n°2 : Vérifier une fonction avec assert

Tester :

🐍 Script Python
def carre(x):
    return x * x

assert carre(3) == 9  # Vérifie que le carré de 3 est bien 9
assert carre(0) == 0  # Vérifie que le carré de 0 est bien 0
assert carre(-2) == 4 # Vérifie que le carré de -2 est bien 4
Python

###

Solution

Résultat : (Aucune sortie si tout est correct !)

Explication :

  • Si tous les tests passent, le programme continue.

  • Si un test échoue, Python affiche une erreur et s’arrête.

Activité n°3 : Vérifier une division euclidienne

Tester :

🐍 Script Python
def division_euclidienne(a, b):   
    if a >= 0 and b > 0 and type(a) == int and type(b) == int:   
        return a // b, a % b   # Quotient et reste
    else:   
        return -1   # Code d'erreur si les valeurs ne sont pas correctes

if __name__ == '__main__':
    assert division_euclidienne(10, 2) == (5, 0)
    assert division_euclidienne(-10, 7) == -1
    assert division_euclidienne(10.3, 4) == -1
    assert division_euclidienne(3, 0) == -1
Python

###

Explication :

  • assert teste plusieurs cas et arrête le programme si un test échoue.

  • La condition if __name__ == '__main__': garantit que ces tests ne s’exécutent que si le fichier est exécuté directement.

Activité n°4 : Vérifier une division euclidienne

Tester :

🐍 Script Python
def division_euclidienne(a, b):   
    if a >= 0 and b > 0 and type(a) == int and type(b) == int:   
        return a // b, a % b   # Quotient et reste
    else:   
        return -1   # Code d'erreur si les valeurs ne sont pas correctes

if __name__ == '__main__':
    assert division_euclidienne(10, 2) == (5, 1)
    assert division_euclidienne(-10, 7) == -1
    assert division_euclidienne(10.3, 4) == -1
    assert division_euclidienne(3, 0) == -1
Python

###

Solution

Explication :

assert division_euclidienne(10, 2) == (5, 1) # Test invalide, Python montre une erreur, car le reste de 10 divisé par 2 est 0 et non 1.

2.2. Les tests avec doctest

⚓︎

doctest permet de vérifier automatiquement que les exemples donnés dans la docstring sont corrects.

Activité n°5 : Tester avec doctest

Tester :

🐍 Script Python
import doctest

def factorielle(n):
    """
    Fonction qui retourne la factorielle d'un nombre.
    :param n: int
    :return: int
    CU (conditions d'utilisation) : n >= 0

    >>> factorielle(0)
    1
    >>> factorielle(4)
    24
    """
    resultat = 1
    for i in range(2, n + 1):
        resultat = resultat * i
    return resultat

doctest.testmod()
Python

ALLER SUR BASTHON

Solution

Explication :

  • doctest recherche les triples chevrons >>> dans la docstring.

  • Il exécute les exemples et compare le résultat attendu et le résultat réel.

  • Si tout est correct, rien ne s’affiche.


Activité n°6 : Introduire une erreur dans doctest

Tester :

🐍 Script Python
import doctest

def factorielle(n):
    """
    Fonction qui retourne la factorielle d'un nombre.
    :param n: int
    :return: int
    CU (conditions d'utilisation) : n >= 0

    >>> factorielle(0)
    100000  # ERREUR VOLONTAIRE
    >>> factorielle(4)
    24
    """
    resultat = 1
    for i in range(2, n + 1):
        resultat = resultat * i
    return resultat

doctest.testmod()
Python

ALLER SUR BASTHON

Solution

Explication :

  • Doctest va signaler une erreur, car factorielle(0) ne renvoie pas 100000.

  • C’est une façon rapide de vérifier si les résultats des tests sont corrects.


Activité n°7 : Activer le mode verbose dans doctest

Tester :

🐍 Script Python
import doctest

def factorielle(n):
    """
    Fonction qui retourne la factorielle d'un nombre.
    :param n: int
    :return: int
    CU (conditions d'utilisation) : n >= 0

    >>> factorielle(0)
    1
    >>> factorielle(4)
    24
    """
    resultat = 1
    for i in range(2, n + 1):
        resultat = resultat * i
    return resultat

doctest.testmod(verbose=True)
Python

ALLER SUR BASTHON

Solution

Explication :

  • En mode verbose, doctest affiche tous les tests, qu’ils réussissent ou échouent.

  • Utile pour voir ce qui est testé.

3. Les préconditions et les postconditions

⚓︎

Les assertions permettent de vérifier les résultats tout au long de l'exécution d'un programme, et pas seulement test par test.

Activité n°8 :

Tester :

🐍 Script Python
from math import sqrt

def square_root(x):
    """
    Fonction qui calcule la racine carrée d'un nombre positif.
    :param x: int ou float (doit être >= 0)
    :return: float
    Précondition : x >= 0
    Postcondition : Le carré du résultat doit être égal à x
    """
    assert x >= 0, "Précondition non respectée : x doit être positif ou nul"
    racine = sqrt(x)
    assert racine**2 == x, "Postcondition non respectée : erreur dans le calcul"
    return racine

print(square_root(25))  # Doit afficher 5.0
print(square_root(-25)) # Doit lever une erreur
Python

###

Solution

Explication :

  • Précondition assert x >= 0 : empêche le calcul si x est négatif.

  • Postcondition assert racine**2 == x : vérifie la validité du résultat.

  • Si une précondition ou postcondition échoue, le programme s’arrête immédiatement avec un message d’erreur.

Pour plus de précisions : https://www.youtube.com/watch?v=DRVoh5XiAZo

4. Les bibliothèques ou modules

⚓︎

4.1. Qu’est ce que c’est ?

⚓︎

Une bibliothèque en Python est un fichier contenant du code réutilisable. Ce code peut être sous forme de fonctions, classes ou variables. Pour utiliser une bibliothèque, on doit l’importer dans notre programme.

Une bibliothèque est une collection de modules. C'est un ensemble plus large qui peut contenir plusieurs fichiers Python, chacun étant un module.

4.2. Importer des bibliothèques (modules) standards

⚓︎

Voici un exemple d’utilisation d’une bibliothèque Python standard, le module math qui contient des fonctions mathématiques comme sqrt() (racine carrée) ou random qui contient randint. On peut noter les 2 manières d’importer le module :

  • import nom_du_module

  • from nom_du_module import nom_de_la_fonction

📋 Texte
>>> import math as m
>>> m.sqrt(25) 

>>> import math
>>> math.sqrt(25) 

>>> from math import sqrt
>>> sqrt(25) 

>>> import random 
>>> x = random.randint(0,10)  

>>> from random import randint 
>>> randint(0,10) 

>>> from random import * 
>>> randint(0,10) 
>>> randrange(10,20,3) #retourne un entier entre 10 et 19 et un pas =3 
Activité n°9 :

Tester :

🐍 Script Python
import math
import random

print(math.sqrt(25))  # Racine carrée de 25

x = random.randint(0, 10)  # Génère un nombre aléatoire entre 0 et 10
print("Nombre aléatoire :", x)
Python

###

Solution

Explication :

  • import math permet d’accéder aux fonctions mathématiques.

  • import random permet d'utiliser des fonctions pour générer des nombres aléatoires.

5. Gestion des exceptions

⚓︎

Ce sont les erreurs que peut rencontrer Python en exécutant le programme. Ces erreurs peuvent être interceptées très facilement et dans certains cas indispensables.

5.1. Lever d’exception

⚓︎

Une exception est une erreur qui se produit lors de l’exécution du programme. Si elle n'est pas gérée, le programme s’arrête brutalement.

Activité n°10 :

Tester :

🐍 Script Python
print(1 / 0)  # Division par zéro
Python

###

Solution

Explication :

  • Python lève une exception ZeroDivisionError, car on ne peut pas diviser par zéro.

5.2. Gestions des exceptions

⚓︎

On peut éviter l'arrêt brutal d'un programme en capturant les erreurs avec try et except.

5.2.1. Forme minimale du bloc try

⚓︎

Activité n°11 : Gestion des erreurs d’entrée utilisateur
🐍 Script Python
annee = input('Entrer une année : ')

try:
    annee = int(annee)  # Convertit l'entrée en entier
    print("Pas de problème :", annee)
except ValueError:
    print("Erreur : Vous devez entrer un nombre entier.")
Python

###

Solution

Explication :

  • Si l’utilisateur entre un nombre, tout fonctionne.

  • Si l’utilisateur entre du texte, une erreur est capturée et un message explicite s’affiche.

5.2.2. Interception d’exceptions particulières

Dans le cas d’une division :
⚓︎

On peut capturer différents types d’erreurs séparément.

Activité n°12 : Gérer plusieurs erreurs
🐍 Script Python
numerateur = input('Numérateur ? ')
denominateur = input('Dénominateur ? ')

try:
    resultat = int(numerateur) / int(denominateur)
except ValueError:
    print("Erreur : Vous devez entrer des nombres.")
except ZeroDivisionError:
    print("Erreur : Division par zéro impossible.")
else:
    print("Le résultat est :", resultat)
Python

###

Solution

Explication :

  • ValueError : L'utilisateur a saisi un texte au lieu d’un nombre.

  • ZeroDivisionError : L’utilisateur a essayé de diviser par zéro.

  • else : S’exécute uniquement si aucun except n’a été déclenché.

5.2.3. finally : Toujours exécuté

⚓︎

Activité n°13 : Utilisation de finally
🐍 Script Python
numerateur = input('Numérateur ? ')
denominateur = input('Dénominateur ? ')

try:
    resultat = int(numerateur) / int(denominateur)
except ValueError:
    print("Erreur : Vous devez entrer des nombres.")
except ZeroDivisionError:
    print("Erreur : Division par zéro impossible.")
else:
    print("Le résultat est :", resultat)
finally:
    print("Fin de l’exécution du programme.")
Python

###

Solution

Explication :

  • finally affiche un message final, que le programme ait réussi ou non.

5.2.4. Lever une exception avec raise

⚓︎

On peut déclencher une exception volontairement si une valeur est incorrecte.

Activité n°14 : Déclencher une erreur volontairement
🐍 Script Python
annee = input("Saisissez une année supérieure à 0 : ")

try:
    annee = int(annee)
    if annee >= 3000 or annee < 0:
        raise ValueError("L'année doit être comprise entre 0 et 2999.")
except ValueError as e:
    print("Erreur :", e)
Python

###

Solution

Explication :

  • Si l’utilisateur entre 3000, l’exception est déclenchée volontairement.

  • Le message d’erreur est personnalisé.

6. Exercices

⚓︎

=> CAPYTALE Le code vous sera donné par votre enseignant

Exercice 1

On considère la fonction multiplier_par_deux(x) qui prend en paramètre x et qui renvoie son double. Ecrire un script de cette fonction avec :

  • Sa documentation dans le docstring comme dans le cours. Ne pas mettre d’exemples

  • 4 tests en assert : avec 0, avec -1, avec 3.2 et avec ‘a’

Exercice 2

On considère une fonction somme_carres(x) qui prend en paramètre x (entier strictement positif) et renvoie la somme des x premiers carrés non nuls.

Ecrire un script de cette fonction avec :

  • Sa documentation dans le docstring comme dans l’activité 1. Ne pas mettre d’exemples

  • 3 tests en assert (comme dans l’activité 3) : avec 1, avec 2 et avec 3

Exercice 3

La fonction précédente à pour condition d’utilisation : x doit être strictement positif. Déclencher une exception et capturer là si le nombre entrée est 0 ou négatif.

Exemple :

📋 Texte
>>> somme_carres(5)
55

>>> somme_carres(0)
Le nombre entré est nul ou négatif

>>> somme_carres(-2)
Le nombre entré est nul ou négatif

Exercice 4

On part du script suivant qui permet d’inverser un nombre

🐍 Script Python
try:
    chaine = input('Entrer un nombre : ') # permet de pouvoir gérer l’exception ValueError
    nombre = float(chaine)
    inverse = 1.0/nombre
except ValueError:
    #ce bloc est exécuté si une exception de type ValueError est levée dans le bloc try
    print(chaine, "n'est pas un nombre !")

except ZeroDivisionError:
#ce bloc est exécuté si une exception de type ZeroDivisionError est levée dans le bloc try
    print("Division par zéro !")
else:
    #on arrive ici si aucune exception n'est levée dans le bloc try
    print("L'inverse de", nombre, "est :", inverse) 

Compléter le script précédent de manière à ressaisir le nombre en cas d’erreur. On pourra englober le script dans une boucle while. Par exemple :

Tester avec :

  • 'a'

  • relancer le programme et tester avec => 'salut !'

  • relancer le programme et tester avec => 0

  • relancer le programme et tester avec => 2

Aide : penser à une boucle infinie et au mot clé break.

Exercice 5

Ecrire un script qui calcule la racine carrée d’un nombre, avec gestion des exceptions. Utiliser la fonction sqrt() du module math.

Par exemple :

📋 Texte
Entrer un nombre : >? go
go n'est pas un nombre valide !
Entrer un nombre : >? -5.26
-5.26 n'est pas un nombre valide !
Entrer un nombre : >? 16
La racine de  16.0 est : 4.0

Tester avec :

  • "go"

  • relancer le programme et tester avec => -5.26

  • relancer le programme et tester avec => 16

Remarquez bien qu’on demande le nombre à l’utilisateur jusqu'à ce qu’il convienne.

Documentation sur les exceptions Source : Fabrice Sincère - Contenu sous licence CC BY-NC-SA 3.0