Mise au point de programmes

Numérique et sciences informatiques

python

Une fonction en python est assez proche d'une fonction au sens mathématique du terme, elle reçoit généralement des données en entrée et retournent généralement en sortie le résultat du traitement opéré par la fonction.

Types de fonctions

On distingue deux types de fonctions :

  • les fonctions natives prête à l'emploi ;
  • les fonctions créées par l'utilisateur  qui permettent d’enrichir le langage.

L’emploi de fonctions permet de décomposer des tâches complexes en tâches élémentaires plus simples et permettent de pouvoir être appelés plusieurs fois.

Spécifications fonctionnelles

Les préconditions correspondent aux prérequis d'emploi d'une fonction et les postconditions concernent ce qui est attendu des résultats retournés par une fonction.

Nous pouvons intégrer des vérifications de nos spécifications dans le corps de notre fonction de différentes manières, l’emploi d'assertions peut nous aider dans ce contexte.

Le mot clé « assert » du langage Python nous permet de tester si une condition est vraie et de générer une erreur dans le cas contraire.

Il est indispensable de documenter ses fonctions en incluant, dans la définition de la fonction, une chaine de caractères (« docstring » en anglais) qui présente synthétiquement la fonction, ses paramètres et les résultats qu'elle produit en retour.

Ainsi placée, notre docstring permet à Python d'afficher avec help() le message d'aide de notre fonction exactement comme pour les fonctions natives du langage.

Les spécifications et la documentation des fonctions contribuent à produire un code informatique de qualité.

La documentation

Il est important de bien distinguer commenter et documenter.

  • les commentaires sont destinés au programmeur, pour expliquer ce que l'on a fait, pourquoi on l'a fait.
  • la documentation est destinée aux utilisateurs, elle leur permet d'expliquer comment on utilise le programme du point de vue de l'utilisateur et non du programmeur.
  • Les docstrings sont des chaînes de commentaires qui doivent être placées juste en dessous des définitions de fonction ou de classe, ou bien tout en haut d'un module. L'intérêt, c'est que les docstrings sont récupérables dynamiquement via l'attribut __doc__, ou grâce à la fonction primitive help(ma_fonction). Cela permet notamment de faire de la documentation pour aider vos utilisateurs et vous-même avec la documentation de votre projet.

    def addition(a, b):
      """
      Cette fonction est une fonction de test
      Elle sert a calculer a + b
      :param a : Valeur 1
      :param b : Valeur 2
      :return : Somme des Valeur 1 et Valeur 2
      :CU : a et b entiers.
      """
      return a + b

    On peut accéder à la Docstring de la fonction addition(a,b) écrite précédemment comme ceci.

    help(addition)
    Help on function addition in module __main__:
    
    addition(a, b)
        Cette fonction est une fonction de test
        Elle sert a calculer a + b
        :param a : Valeur 1
        :param b : Valeur 2
        :return : Somme des Valeur 1 et Valeur 2
        :CU : a et b entiers.

    Les commentaires

    De bons commentaires vous feront gagner beaucoup de temps sur les projets car

    • ils facilitent l'examen du code par une personne tiers (dans le cadre d'un travail collaboratif par exemple)
    • ils vous seront utiles si vous replongez dans votre code après plusieurs mois d'interruption

    Voici un exemple de ce qu'on peut proposer comme amélioration. Il est éclairé par des commentaires qui mettent en relief l'algorithme utilisé.

    def tri_selection(tableau: list) -> list:
        tableau_trie = tableau[:]
        longueur = len(tableau_trie)
        for position in range(0, longueur):
            #### invariant de boucle ####
            # tableau_trie est trié des indices 0 à position (exclu)
            # à l'indice position se trouvera le minimum de la fin du tableau
    
            # recherche du min à partir de position+1
            for j in range(position+1, longueur):
                if tableau_trie[j] < tableau_trie[position]:
                    # on a trouvé en j une valeur inférieure, on échange avec position
                    tableau_trie[position], tableau_trie[j] = tableau_trie[j], tableau_trie[position]
    
        return tableau_trie

    Les tests

    Une bonne pratique est d'écrire des procédures automatisées de tests avant d'écrire la fonction ou le programme que l'on souhaite produire.
    Écrire un bon jeu de tests n'est pas si simple. Cela prend du temps mais au final, ce temps est largement récupéré car les tests étant automatisés, ils sont rapides à réaliser, y compris après chaque modification du code de votre programme. Détecter un bug et le résoudre prend souvent plus de temps que de se poser et réfléchir à des tests pertinents, surtout quand votre programme est composé de multiples fonctions susceptibles de poser problèmes. Si vous avez des tests unitaires pour chacune de vos fonctions, les tests vous indiqueront où se trouve le problème.

    Le mécanisme d'assertion est là pour empêcher des erreurs qui ne devraient pas se produire, en arrêtant prématurément le programme, avant d'exécuter le code qui aurait produit une erreur.

    assert test_booleen,"texte à afficher si test_booleen est faux"

Un exemple :

def est_pair(a) :
    """ Teste si un nombre entier est pair par le reste de la division entière entre le nombre et 2.
    :paramètre a: entier (int)
    :return :un booléen (boolean) , True si a est pair, False sinon

    CU : a >= 0

    Exemples
    >>> est_pair(6)
    True
    >>> est_pair(0)
    True
    >>> est_pair(1)
    False
    >>> est_pair(-4)
    Traceback (most recent call last):
    ...
    AssertionError: l'argument doit être un entier positif
    """
    assert(a>=0 and type(a)==int), "l'argument doit être un entier positif"

    if a % 2 == 0 :
    	test = True
    else :
    	test = False

    return test

Le module doctest permet d’intégrer des tests dans la docstring des fonctions. Les doctests sont repérées par la chaîne >>>. Écrire des doctests permet à la fois de réaliser des tests unitaires, mais aussi de documenter efficacement la fonction.

import doctest
doctest.testmod(verbose=True) # Pour avoir le compte rendu

Bibliothèques

Les bibliothèques sont des conteneurs permettant d'étendre les fonctionnalités initiales du langage, en apportant des outils supplémentaires en fonction des besoins.

Le langage Python inclut en standard un grand nombre de modules spécialisés qui composent sa bibliothèque standard. L'importation d'un tel module s'effectue avec l'instruction « import ».

Si la bibliothèque souhaitée ne fait pas partie de la bibliothèque standard de Python, elle devra être installée séparément.

La documentation officielle disponible en ligne présente le contenu des différents modules et le fonctionnement des éléments de ces modules.

Une aide hors ligne est également disponible directement sur votre machine avec la fonction native help().

Pour une vue synthétique sur un module il est possible de faire appel à la fonction native dir() qui retourne la liste de toutes les fonctions du module.


Savoir faire

  • Prototyper une fonction
  • Décrire les preconditions sur les arguments et les postconditions sur les résultats
  • Utiliser les jeux de tests
  • Utiliser la documentation d'une bibliothèque

Fiche de cours