C7.1 : Lisibilité du code

Les bonnes pratiques

Généralités

■ Un code lisible

Un code bien lisible doit contenir :

  • - des commentaires (ni trop peu, ni trop !) ;
  • - des noms de variables, des noms de fonctions... qui aident à la compréhension du programme ;
  • - des annotations de type pour les fonctions ;
  • - des docstrings pour les fonctions, pour les classes, pour les modules...

■ Un code structuré

Dans l'idéal, le code doit être structuré avec des fonctions. L'idée directrice est :

une action cohérente = une fonction

Annotations de type

Les annotations de type (typing hints, en anglais) permettent à l'utilisateur d'une fonction de savoir quels sont les types des paramètres et quel est le type de la valeur renvoyée, et ce de façon concise en écriture.

Remarque : les annotations de type ont été introduites dans la versions 3.5 de Python.

Exemple

def maxi(l:list) -> float:
    if l == []:
        return None
    else:
        nb_max = l[0]
        for i in range(1, len(l)):
            if l[i] > nb_max:
                nb_max = l[i]
        return nb_max

Remarques

Les annotations de type sont encore en évolution.

• A partir de la versin 3.9 de python, il est possible de préciser le type des objets que doit contenir une collection (tuple, list, dict).

def maxi(l:list[float]) -> float:
    if l == []:
        return None
    else:
        nb_max = l[0]
        for i in range(1, len(l)):
            if l[i] > nb_max:
                nb_max = l[i]
        return nb_max

• A partir de la version 3.10, il est possible d'utiliser l'opérateur | pour indiquer plusieurs types possibles.

def somme(a:int|float, b:int|float)->int|float:
    return a + b

Docstrings

■ Emplacement des docstrings

Voici un exemple de code d'un module avec l'emplacement des docstrings :

"""
Docstring du module lorsque le module est importé dans un autre fichier.
"""

class MaClasse:
    """
    Docstring de la classe (avec les informations sur les paramètres du constructeur de la classe)
    """
    def __init__(self, args):
        pass

    def methode_de_maclasse(self, args):
        """
        Docstring de la méthode de classe
        """
        pass


def ma_function():
    """
    Docstring de la fonction
    """
    pass

■ Contenu de la docstring d'une fonction

Question 1 : Rappeler la structure de la docstring d'une fonction.

Afficher la correction

La docstring doit contenir :

  • - Une phrase de description du rôle de la fonction ;
  • - Les informations sur les paramètres : nom, type, description et préconditions ;
  • - Les informations sur ce que retourne la fonction : type et description
  • - Le détail des éventuels effets de bord.

Exemple :

'''
Rôle de la fonction
Parameters:
    p1 : (type1) Description
    p2 : (type2) Description
    ...
Returns:
    (type) Description

Side effects:
    Description
'''

■ Utilisation d'une docstring

Question 2 : Comment peut-on, en ligne de commande, afficher le contenu de la docstring d'un module, d'une classe ou d'une fonction ?

Afficher la correction

La fonction help(...) permet d'afficher le contenu de la docstring d'un module, d'une classe ou d'une fonction.

Exemple :

>>> help(len)

Applications

Application 1 (sur papier)

Améliorer la lisibilité du code suivant :

def f(t):
    a = 0
    for i in range(len(t)):
        a = a + t[i]
    return a
Afficher la correction
def indice_max_liste(liste:list)->float:
    '''
    Renvoie la somme des valeurs d'une liste de nombres
    Parameters:
        liste : (list) Liste de nombres
    Returns:
        (float) Somme des valeurs de la liste
    '''
    somme = 0
    for i in range(len(liste)):
        somme = somme + liste[i]
    return somme

Application 2 (sur papier)

Écrire le code de la fonction dont la docstring est la suivante :

'''
Calcule et renvoie la moyenne (avec deux chiffres après la virgule) des valeurs d'un tableau de nombres
Parameters:
    t : (list) Liste de nombres
Returns:
    (float) Moyenne arrondie à 2 chiffres après la virgule
'''
Afficher la correction
def moyenne(liste_nombres):
    '''
    Calcule et renvoie la moyenne (avec deux chiffres après la virgule) des valeurs d'un tableau de nombres
    Parameters:
        t : (list) Liste de nombres
    Returns:
        (float) Moyenne arrondie à 2 chiffres après la virgule
    '''
    somme = 0
    for nombre in liste_nombres:
        somme = somme + nombre
    moyenne = somme / len(liste_nombres)
    return round(moyenne, 2)


liste = [5.5, 10.5, 15]
print(moyenne(liste))

Application 3 (sur ordinateur)

Quels sont les premiers mots de la docstring du module random ?

Application 4

Améliorer la lisibilité du code ci-dessous (entre autre, en utilisant des fonctions).

Li = [[1, 1, 1, 0, 1, 1, 1], [1, 0, 1, 0, 0, 0, 1], [1, 0, 1, 1, 1, 0, 1], [1, 0, 0, 0, 0, 0, 1], [1, 1, 1, 0, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1], [1, 1, 1, 0, 1, 1, 1]]
for i in range(len(Li)):
    x = ""
    for j in Li[i]:
        if j:
            x = x + "• "
        else:
            x = x + "  "
    print(x)
Afficher la correction
def listeligne_to_chaine(liste_ligne:list) -> str:
    """
    Converti une liste de valeurs en une chaine à afficher
    """
    chaine = ""
    for val in liste_ligne:
        if val:
            chaine = chaine + "• "
        else:
            chaine = chaine + "  "
    return chaine

def listelistes_to_chaine(listelistes:list) -> str:
    """
    Convertie une liste de listes (de valeurs) en une chaine à afficher
    """
    chaine_globale = ""
    for liste_ligne in listelistes:
        chaine_ligne = listeligne_to_chaine(liste_ligne)
        chaine_globale = chaine_globale + chaine_ligne + "\n"
    return chaine_globale

lili = [[1, 1, 1, 0, 1, 1, 1],
        [1, 0, 1, 0, 0, 0, 1],
        [1, 0, 1, 1, 1, 0, 1],
        [1, 0, 0, 0, 0, 0, 1],
        [1, 1, 1, 0, 1, 1, 1],
        [1, 0, 0, 0, 0, 0, 1],
        [1, 1, 1, 0, 1, 1, 1]]
print(listelistes_to_chaine(lili))