N.S.I. WorkSpace T-Th-D,Terminale D.5 – Mise au point des programmes. Gestion des bugs

D.5 – Mise au point des programmes. Gestion des bugs

Categories:

Une première (mais pas dernière!) situation « critique » !

Soit les instructions suivantes :

def ajoute(tableau, valeur) :
	return tableau + valeur

# ========== Test ============
x = 5
t = [0,4,8,9]
t = ajoute(t, x)

Selon vous, si on exécute ces instructions dans un éditeur/interpréteur de langage de programmation Python, que va-t-on obtenir comme résultat dans la console ?

Dans un éditeur/interpréteur de langage de programmation Python, entrer et exécuter ces instructions.
Le résultat obtenu est-il conforme à celui que vous aviez prévu juste avant ? Précisez.

Quelle point de vigilance dans l’écriture d’un programme est soulevé par cet exemple ?

Mettre au point un programme et gérer les bugs consiste principalement à s’assurer que les pré-conditions soient remplies, c’est à dire s’assurer de la validité des données fournies en entrée (constantes, variables, arguments des fonctions).

Une donnée en entrée est valide si son type et son domaine sont conformes aux attendus du traitement que l’on veut effectuer ensuite.

Ceci est d’autant plus important quand ces donnes sont fournies par l’utilisateur dont il est difficile de prévoir s’il respectera ou non les indications qui lui seront fournies.

Effectuer des tests : gérer et contrôler le type des variables

Objectifs d’apprentissage ou d’entraînement et pré-requis (acquis antérieurs mobilisés)

  • Exception « TypeError »
  • Notions d’assertion et d’exception.

Programmation en langage Python : compétences requises ( à acquérir, à mobiliser, à développer, à maîtriser)

  • Instruction ‘assert condition, message affiché si condition fausse’ (NSI Première)
  • Programmation défensive (NSI Première) : ‘if … else … ‘
  • Fonctions natives : ‘type()’, ‘isinstance()’

Ce qu’il faut retenir (CQFR)… donc apprendre !

Dans l’exemple pris en introduction, ce qui s’affiche dans la console est appelée une « exception ».

On dit que cette exception est « levée » lorsque l’interpréteur de code Python rencontre une « erreur » qui rend impossible l’exécution d’une instruction.

Les causes qui amènent une levée d’exception sont nombreuses.

Dans l’exemple pris en introduction, l’exception est « TypeError » : elle est levée par l’instruction ‘return tableau + valeur’, dans la mesure où la variable ‘tableau’ est de type ‘list’ et ‘valeur’ de type ‘int’. L’opérateur ‘+’ qui suit la variable ‘tableau’ vaut donc comme une ‘concaténation’ . Si l’instruction était ‘return valeur + tableau’, ce même opérateur vaudrait comme une ‘addition’

Gestion de l’exception « TypeError »

Documenter module et fonction, annoter les variables et arguments

Référence complète sur les normes de documentation (« Numpy ») : consulter.

Docstring au format « Numpy » sans annotation.

def ajoute(tableau, valeur):
    """Ajoute par concaténation 'valeur' à la suite de 'tableau'.
    
    Parameters
    ----------
    tableau : LIST
        tableau de données.
    valeur : LIST
        Tableau de données.

    Returns
    -------
    LIST
        Tableau comportant les données des deux tableaux.
    """
    return tableau + valeur

Docstring au format « Numpy » avec annotation.

def ajoute(tableau: list, valeur:list)-> list:
    """Ajoute par concaténation 'valeur' à la suite de 'tableau'.
    
    Parameters
    ----------
    tableau
        tableau de données.
    valeur
        Tableau de données.

    Returns
    -------
    LIST
        Tableau comportant les données des deux tableaux.
    """
    return tableau + valeur
  • Modifier le programme de l’exemple pris en introduction :
    • pour que l’exception ne soit plus levée
    • pour que la valeur passée en argument soit ajoutée à la liste quand cela est possible, en conservant l’opérateur ‘ + ’
def ajoute(tableau: list, valeur: list)-> list:
  	assert isinstance(tableau, list), "Le paramètre 'tableau' doit être de type 'list'"
    assert isinstance(valeur, list), "Le paramètre 'valeur' doit être de type 'list'"
	return tableau + valeur

# ========== Test ============
x = 5
t = [0,4,8,9]
t = ajoute(t, x)
def ajoute(tableau: list, valeur: list)-> list:
  	assert isinstance(tableau, list), "Le paramètre 'tableau' doit être de type 'list'"
    
    # Transtypage (changement de type)
    if isinstance(valeur, int) or isinstance(valeur, float)  or isinstance(valeur, str):
      	valeur = [valeur]
    if isinstance(valeur, tuple):
        valeur = list(valeur)
        
    assert isinstance(valeur, list), "Le paramètre 'valeur' doit être de type 'list'" 
	return tableau + valeur

# ========== Test ============
x = 5
t = [0,4,8,9]
t = ajoute(t, x)
def ajoute(tableau: list, valeur: list)-> list:
  
  	try:
      	return tableau + valeur
      
    except TypeError:
        try:
             return tableau + [valeur]
        except TypeError:
             assert isinstance(valeur, list), "Le paramètre 'valeur' doit être de type 'list'"

Gérer le nombre de paramètres d’une fonction

Rappels sur les fonctions

Source (en)

  • On appelle paramètre(s) la ou les variables définies entre les parenthèses qui suivent le nom de la fonction lorsqu’on la définit.

Par exemple :

def addition(a, b, c):
       return a + b + c

La fonction ‘addition’ est définie avec trois paramètres : ‘a’, ‘b’ et ‘c’.

  • On appelle argument(s) la ou les valeurs transmises aux paramètres de la fonction lorsqu’on appelle la dite fonction.

Par exemple :

x = addition(15, 5, 8)

La fonction est appelée avec trois arguments : 15, 5 et 8.

Par défaut, le nombre d’arguments donnés lors de l’appel de la fonction doit être identique au nombre de paramètres.

Dans l’exemple précédent les arguments sont dits ‘positionnels‘ ce qui signifie que les arguments seront affectés aux paramètres en respectant l’ordre de leur position.

Par exemple :
def addition(a, b, c):
return a + b + c

x = addition(15, 5, 8)
Dans ce cas, a prend la valeur 15, b la valeur 5 et c la valeur 8.
Et donc la fonction renverra 15 + 5 + 8.

Toutefois il est possible d’appeler la fonction en nommant les arguments. On parle d’arguments nommés.
x = addition(c=15, a=5, b=8)
Dans ce cas, a prend la valeur 5, b la valeur 8 et c la valeur 15.
Et donc la fonction renverra 5 + 8 + 15.
Comme on peut le constater, la ‘position’ (ou l’ordre de succession) des arguments n’a plus d’importance.

Il est également possible d’appeler une fonction en donnant moins d’arguments que de paramètres s’il existe une valeur par défaut pour ces derniers. On parle de paramètre par défaut.
Les paramètres ayant des valeurs par défaut doivent être positionnés en dernier.

Par exemple :
def addition(a, b, c=8):
return a + b + c

x = addition(15, 5)
Dans ce cas a prend la valeur 15, b la valeur 5 et c la valeur 8.
Attention : le nombre d’arguments passés lors de l’appel de la fonction doit être identique au nombre de paramètres positionnels et ces arguments doivent être placés dans l’ordre de position des paramètres.

*args et **kwargs

Il est possible d’indiquer un nombre variable de paramètres positionnels en écrivant *args.

Lors de l’appel de la fonction les arguments seront affectés à la variables ‘args’ sous la forme d’un tuple.

Par exemple :

def addition(*args):
      s = 0
      for e in args:
             s += e
      return s

addition(5, 8, 7, 5) renvoie 5 + 8 + 7 + 5 = 25
addition(2, 4) renvoie 2 + 4 = 6

Il est possible d’indiquer un nombre variable de paramètre nommés en écrivant **kwargs.

Lors de l’appel de la fonction les arguments nommés seront affectés à la variables ‘kwargs’ sous la forme d’un dictionnaire

Par exemple :

def addition(**kwargs):
      s = 0
      for k in kwargs:
             # k est une clé de kwargs et kwargs[k] la valeur associée à cette clé
             s += kwargs[k]
      return s

addition(c=5, b=8, a=7, d= 5) renvoie 5 + 8 + 7 + 5 = 25
addition(2, 4) renvoie 2 + 4 = 6
Article sous licence << Cliquez pour plus d’informations <<

Exercices

Exercice 1 – Écrire en langage Python une fonction intList qui prend en argument un nombre entier positif supérieur à zéro n et qui demande à l’utilisateur de saisir n nombres entiers qui seront rangés dans une liste tab (renvoyée par la fonction.)
Par exemple :
intList(5) renvoie [2, 12, 8, 6, 9] pour les valeurs saisies ‘2’, ‘5.6’, ‘xxx’, ’12, 8′, ‘8,8’, ‘6’ et ‘9’.
intList(0) ou intList(-4) ou intList(2.5) ou intList(« NSI ») lève un exception et affiche « Argument non valide »

Corrigé

(Réponse possible)
def intList(n: int)-> list:
    assert isinstance(n, int) and n > 0,"Argument non valide"
    tab = []
    ind = 1
    msg = "Saisir un 1er nombre entier :"
    while n > 0:
        val = input(msg)
        try:
            val = int(val)
            n -= 1
            tab.append(val)
            ind += 1
            msg = "Saisir un " + str(ind) +"e nombre entier :"
        except:
            continue
    return tab

# Programme principal

t = intList(4)
# t = intList(0)
# t = intList(-4)
# t = intList("4")

Exercice 2 – Écrire en langage Python une fonction fltList qui prend en argument un nombre entier positif supérieur à zéro n et qui demande à l’utilisateur de saisir n nombres réels qui seront rangés dans une liste tab (renvoyée par la fonction.)
On rappelle que la méthode ‘replace(str1, str2)’ permet de remplacer la chaine ‘str1’ par la chaine ‘str2’ dans une instance de la classe ‘str’. Par exemple : « Je mange des pommes ».replace(« pommes », « fraises ») renvoie « Je mange des fraises ».
Par exemple :
fltList(3) renvoie [2.0, 5.6, 8.8] pour les valeurs saisies ‘2’, ‘5.6’, ‘xxx’, 8,8′.
fltList(0) ou fltList(-4) ou fltList(2.5) ou fltList(« 2« ) lève un exception et affiche « Argument non valide »

Corrigé

(Réponse possible)
def fltList(n: int)-> list:
    assert isinstance(n, int) and n > 0,"Argument non valide"
    tab = []
    ind = 1
    msg = "Saisir un 1er nombre réel :"
    while n > 0:
        val = input(msg)
        val = val.replace(",", ".")
        try:
            val = float(val)
            n -= 1
            tab.append(val)
            ind += 1
            msg = "Saisir un " + str(ind) +"e nombre réel :"
        except:
            continue
    return tab

# Programme principal

t = fltList(4)
# t = fltList(0)
# t = fltList(-4)
# t = fltList(2.5)
# t = fltList("4")

Exercice 3 – Écrire en langage Python une fonction addressIP qui prend en argument un quadruplet de nombres entiers positifs compris entre 0 et 255 et qui renvoie une adresse IP sous la forme d’une chaîne de caractères avec les valeurs du quadruplet en notation pointée.
Par exemple :
addressIP((24, 65, 124, 250)) renvoie ‘24.65.124.250’

addressIP(24, 65, 124, 250) lève une exception et affiche ‘Argument invalide. La fonction ne prend qu’un seul argument de type quadruplet.

addressIP((24, 65, 300, 250)) ou addressIP((2.4, 65, 300, 250)) ou addressIP((24, 65, ‘xxx’ , 250)) ou
addressIP((-24, 65, 124 , 250)) lève une exception et affiche ‘Argument invalide. Au moins l’une des valeurs du quadruplet donné en argument n’est pas un entier positif compris entre 0 et 255.« 

Corrigé

(Réponse possible)
Article sous licence << Cliquez pour plus d’informations <<