Apprenez à programmer en Python
Last updated on Monday, September 8, 2014
  • 4 semaines
  • Facile

Free online content available in this course.

Paperback available in this course

eBook available in this course.

Certificate of achievement available at the end this course

Got it!

De bonnes pratiques

Nous allons à présent nous intéresser à quelques bonnes pratiques de codage en Python.

Les conventions que nous allons voir sont, naturellement, des propositions. Vous pouvez coder en Python sans les suivre.

Toutefois, prenez le temps de considérer les quelques affirmations ci-dessous. Si vous vous sentez concernés, ne serait-ce que par une d'entre elles, je vous invite à lire ce chapitre :

  • Un code dont on est l'auteur peut être difficile à relire si on l'abandonne quelque temps.

  • Lire le code d'un autre développeur est toujours plus délicat.

  • Si votre code doit être utilisé par d'autres, il doit être facile à reprendre (à lire et à comprendre).

Pourquoi suivre les conventions des PEP ?

Vous avez absolument le droit de répondre en disant que personne ne lira votre code de toute façon et que vous n'aurez aucun mal à comprendre votre propre code. Seulement, si votre code prend des proportions importantes, si l'application que vous développez devient de plus en plus utilisée ou si vous vous lancez dans un gros projet, il est préférable pour vous d'adopter quelques conventions clairement définies dès le début. Et, étant donné qu'il n'est jamais certain qu'un projet, même démarré comme un amusement passager, ne devienne pas un jour énorme, ayez les bons réflexes dès le début !

En outre, vous ne pouvez jamais être sûrs à cent pour cent qu'aucun développeur ne vous rejoindra, à terme, sur le projet. Si votre application est utilisée par d'autres, là encore, ce jour arrivera peut-être lorsque vous n'aurez pas assez de temps pour poursuivre seul son développement.

Quoi qu'il en soit, je vais vous présenter plusieurs conventions qui nous sont proposées au travers de PEP (Python Enhancement Proposal : proposition d'amélioration de Python). Encore une fois, il s'agit de propositions et vous pouvez choisir d'autres conventions si celles-ci ne vous plaisent pas.

La PEP 20 : tout une philosophie

La PEP 20, intitulée The Zen of Python, nous donne des conseils très généraux sur le développement. Elle est disponible sur le site de Python.

Bien entendu, ce sont davantage des conseils axés sur « comment programmer en Python » mais la plupart d'entre eux peuvent s'appliquer à la programmation en général.

Je vous propose une traduction de cette PEP :

  • Beautiful is better than ugly : le beau est préférable au laid ;

  • Explicit is better than implicit : l'explicite est préférable à l'implicite ;

  • Simple is better than complex : le simple est préférable au complexe ;

  • Complex is better than complicated : le complexe est préférable au compliqué ;

  • Flat is better than nested : le plat est préférable à l'imbriqué. Moins littéralement, du code trop imbriqué (par exemple une boucle imbriquée dans une boucle imbriquée dans une boucle…) est plus difficile à lire ;

  • Sparse is better than dense : l'aéré est préférable au compact ;

  • Readability counts : la lisibilité compte ;

  • Special cases aren't special enough to break the rules : les cas particuliers ne sont pas suffisamment particuliers pour casser la règle ;

  • Although practicality beats purity : même si l'aspect pratique doit prendre le pas sur la pureté. Moins littéralement, il est difficile de faire un code à la fois fonctionnel et « pur » ;

  • Errors should never pass silently : les erreurs ne devraient jamais passer silencieusement ;

  • Unless explicitly silenced : à moins qu'elles n'aient été explicitement réduites au silence ;

  • In the face of ambiguity, refuse the temptation to guess : en cas d'ambiguïté, résistez à la tentation de deviner ;

  • There should be one -- and preferably only one -- obvious way to do it : il devrait exister une (et de préférence une seule) manière évidente de procéder ;

  • Although that way may not be obvious at first unless you're Dutch : même si cette manière n'est pas forcément évidente au premier abord, à moins que vous ne soyez Néerlandais ; % il faudrait peut-être indiquer que c'est une blague ?

  • Now is better than never : maintenant est préférable à jamais ;

  • Although never is often better than *right* now : mais jamais est parfois préférable à immédiatement ;

  • If the implementation is hard to explain, it's a bad idea : si la mise en œuvre est difficile à expliquer, c'est une mauvaise idée ;

  • If the implementation is easy to explain, it may be a good idea : si la mise en œuvre est facile à expliquer, ce peut être une bonne idée ;

  • Namespaces are one honking great idea -- let's do more of those : les espaces de noms sont une très bonne idée (faisons-en plus !).

Comme vous le voyez, c'est une liste d'aphorismes très simples. Ils donnent des idées sur le développement Python mais, en les lisant pour la première fois, vous n'y voyez sans doute que peu de conseils pratiques.

Cependant, cette liste est vraiment importante et peut se révéler très utile. Certaines des idées qui s'y trouvent couvrent des pans entiers de la philosophie de Python.

Si vous travaillez sur un projet en équipe, un autre développeur pourra contester la mise en œuvre d'un extrait de code quelconque en se basant sur l'un des aphorismes cités plus haut.

Quand bien même vous travailleriez seul, il est toujours préférable de comprendre et d'appliquer la philosophie d'un langage quand on l'utilise pour du développement.

Je vous conseille donc de garder sous les yeux, autant que possible, cette synthèse de la philosophie de Python et de vous y référer à la moindre occasion. Commencez par lire chaque proposition. Les lignes sont courtes, prenez le temps de bien comprendre ce qu'elles veulent dire.

Sans trop détailler ce qui se trouve au-dessus (cela prendrait trop de temps), je signale à votre attention que plusieurs de ces aphorismes parlent surtout de l'allure du code. L'idée qui semble se dissimuler derrière, c'est qu'un code fonctionnel n'est pas suffisant : il faut, autant que possible, faire du « beau code ». Qui fonctionne, naturellement… mais ce n'est pas suffisant !

Maintenant, nous allons nous intéresser à deux autres PEP qui vous donnent des conseils très pratiques sur votre développement :

  • la première nous donne des conseils très précis sur la présentation du code ;

  • la seconde nous donne des conseils sur la documentation au cœur de notre code.

La PEP 8 : des conventions précises

Maintenant que nous avons vu des directives très générales, nous allons nous intéresser à une autre proposition d'amélioration, la PEP 8. Elle nous donne des conseils très précis sur la forme du code. Là encore, c'est à vous de voir : vous pouvez appliquer la totalité des conseils donnés ici ou une partie seulement. Vous pouvez retrouver la PEP 8 sur le site de Python.

Je ne vais pas reprendre tout ce qui figure dans cette PEP mais je vais expliquer la plupart des conseils en les simplifiant. Par conséquent, si l'une des propositions présentées dans cette section manque d'explications à vos yeux, je vous conseille d'aller faire un tour sur la PEP originale. Ce qui suit n'est pas une traduction complète, j'insiste sur ce point.

Introduction

L'une des convictions de Guido (Guido Van Rossum, créateur et BDFL, Benevolent Dictator For Life soit « dictateur bienveillant à vie » de Python) est que le code est lu beaucoup plus souvent qu'il n'est écrit. Les conseils donnés ici sont censés améliorer la lisibilité du code. Comme le dit la PEP 20, la lisibilité compte !

Un guide comme celui-ci parle de cohérence. La cohérence au cœur d'un projet est importante. La cohérence au sein d'une fonction ou d'un module est encore plus importante.

Mais il est encore plus essentiel de savoir « quand » être incohérent (parfois, les conseils de style donnés ici ne s'appliquent pas). En cas de doute, remettez-vous en à votre bon sens. Regardez plusieurs exemples et choisissez celui qui semble le meilleur.

Il y a deux bonnes raisons de ne pas respecter une règle donnée :

  1. Quand appliquer la règle rend le code moins lisible.

  2. Dans un soucis de cohérence avec du code existant qui ne respecte pas cette règle non plus. Ce cas peut se produire si vous utilisez un module ou une bibliothèque qui ne respecte pas les mêmes conventions que celles définies ici.

Forme du code

    • Indentation : utilisez 4 espaces par niveau d'indentation.

    • Tabulations ou espaces : ne mélangez jamais, dans le même projet, des indentations à base d'espaces et d'autres à base de tabulations. À choisir, on préfère généralement les espaces mais les tabulations peuvent être également utilisées pour marquer l'indentation.

    • Longueur maximum d'une ligne : limitez vos lignes à un maximum de 79 caractères. De nombreux éditeurs favorisent des lignes de 79 caractères maximum. Pour les blocs de texte relativement longs (docstrings, par exemple), limitez-vous de préférence à 72 caractères par ligne.

Quand cela est possible, découpez vos lignes en utilisant des parenthèses, crochets ou accolades plutôt que l'anti-slash \. Exemple :

appel_d_une_fonction(parametre_1, parametre_2,
        parametre_3, parametre_4):
   ...

Si vous devez découper une ligne trop longue, faites la césure après l'opérateur, pas avant.

# Oui
    un_long_calcul = variable + \
            taux * 100
    
# Non
    un_long_calcul = variable \
            + taux * 100
  • Sauts de ligne : séparez par deux sauts de ligne la définition d'une fonction et la définition d'une classe.

Les définitions de méthodes au cœur d'une classe sont séparées par une ligne vide. Des sauts de ligne peuvent également être utilisés, parcimonieusement, pour délimiter des portions de code

  • Encodage : à partir de Python 3.0, il est conseillé d'utiliser, dans du code comportant des accents, l'encodage Utf-8.

Directives d'importation

  • Les directives d'importation doivent préférentiellement se trouver sur plusieurs lignes. Par exemple :

import os
import sys

plutôt que :

import os, sys

Cette syntaxe est cependant acceptée quand on importe certaines données d'un module :

from subprocess import Popen, PIPE
  • Les directives d'importation doivent toujours se trouver en tête du fichier, sous la documentation éventuelle du module mais avant la définition de variables globales ou de constantes du module.

  • Les directives d'importation doivent être divisées en trois groupes, dans l'ordre :

    1. les directives d'importation faisant référence à la bibliothèque standard ;

    2. les directives d'importation faisant référence à des bibliothèques tierces ;

    3. les directives d'importation faisant référence à des modules de votre projet.

    Il devrait y avoir un saut de ligne entre chaque groupe de directives d'importation.

  • Dans vos directives d'importation, utilisez des chemins absolus plutôt que relatifs. Autrement dit :

from paquet.souspaquet import module

# Est préférable à
from . import module

Le signe espace dans les expressions et instructions

  • Évitez le signe espace dans les situations suivantes :

    • Au cœur des parenthèses, crochets et accolades :

      # Oui
          spam(ham[1], {eggs: 2})
      
      # Non
          spam( ham[ 1 ], { eggs: 2 } )
    • Juste avant une virgule, un point-virgule ou un signe deux points :

      # Oui
          if x == 4: print x, y; x, y = y, x
      
      # Non
          if x == 4 : print x , y ; x , y = y , x
    • Juste avant la parenthèse ouvrante qui introduit la liste des paramètres d'une fonction :

      # Oui
          spam(1)
      
      # Non
          spam (1)
    • Juste avant le crochet ouvrant indiquant une indexation ou sélection :

      # Oui
          dict['key'] = list[index]
      
      # Non
          dict ['key'] = list [index]
    • Plus d'un espace autour de l'opérateur d'affectation = (ou autre) pour l'aligner avec une autre instruction :

      # Oui
          x = 1
          y = 2
          long_variable = 3
      
      # Non
          x             = 1
          y             = 2
          long_variable = 3
  • Toujours entourer les opérateurs suivants d'un espace (un avant le symbole, un après) :

    • affectation : =, +=, -=, etc. ;

    • comparaison : <, >, <=, …, in, not in, is, is not ;

    • booléens : and, or, not ;

    • arithmétiques : +, -, *, etc.

    # Oui
        i = i + 1
        submitted += 1
        x = x * 2 - 1
        hypot2 = x * x + y * y
        c = (a + b) * (a - b)
    
    # Non
        i=i+1
        submitted +=1
        x = x*2 - 1
        hypot2 = x*x + y*y
        c = (a+b) * (a-b)
    # Oui
        def fonction(parametre=5):
            ...
        fonction(parametre=32)
    
    # Non
        def fonction(parametre = 5):
            ...
        fonction(parametre = 32)
  • Il est déconseillé de mettre plusieurs instructions sur une même ligne :

    # Oui
        if foo == 'blah':
            do_blah_thing()
        do_one()
        do_two()
        do_three()
    
    # Plutôt que
        if foo == 'blah': do_blah_thing()
        do_one(); do_two(); do_three()

Commentaires

  • Les commentaires doivent être des phrases complètes, commençant par une majuscule. Le point terminant la phrase peut être absent si le commentaire est court.

  • Si vous écrivez en anglais, les règles de langue définies par Strunk and White dans « The Elements of Style » s'appliquent.

  • À l'attention des codeurs non-anglophones : s'il vous plaît, écrivez vos commentaires en anglais, sauf si vous êtes sûrs à 120% que votre code ne sera jamais lu par quelqu'un qui ne comprend pas votre langue (ou que vous ne parlez vraiment pas un mot d'anglais !).

Conventions de nommage

Noms à éviter

N'utilisez jamais les caractères suivants de manière isolée comme noms de variables : l (L minuscule), O (o majuscule) et I (i majuscule). L'affichage de ces caractères dans certaines polices fait qu'ils peuvent être aisément confondus avec les chiffres 0 ou 1.

Noms des modules et packages

Les modules et packages doivent avoir des noms courts, constitués de lettres minuscules. Les noms de modules peuvent contenir des signes _ (souligné). Bien que les noms de packages puissent également en contenir, la PEP 8 nous le déconseille.

Noms de classes

Sans presque aucune exception, les noms de classes utilisent la convention suivante : la variable est écrite en minuscules, exceptée la première lettre de chaque mot qui la constitue. Par exemple : MaClasse.

Noms d'exceptions

Les exceptions étant des classes, elles suivent la même convention. En anglais, si l'exception est une erreur, on fait suivre le nom du suffixe Error (vous retrouvez cette convention dans SyntaxError, IndexError…).

Noms de variables, fonctions et méthodes

La même convention est utilisée pour les noms de variables (instances d'objets), de fonctions ou de méthodes : le nom est entièrement écrit en minuscules et les mots sont séparés par des signes soulignés (_). Exemple : nom_de_fonction.

Constantes

Les constantes doivent être écrites entièrement en majuscules, les mots étant séparés par un signe souligné (_). Exemple : NOM_DE_MA_CONSTANTE.

Conventions de programmation

Comparaisons

Les comparaisons avec des singletons (comme None) doivent toujours se faire avec les opérateurs is et is not, jamais avec les opérateurs == ou !=.

# Oui
    if objet is None:
        ...

# Non
    if objet == None:
        ...

Quand cela est possible, utilisez l'instruction if objet: si vous voulez dire if objet is not None:.

La vérification du type d'un objet doit se faire avec la fonction isinstance :

# Oui
    if isinstance(variable, str):
        ...

# Non
    if type(variable) == str:
        ...

Quand vous comparez des séquences, utilisez le fait qu'une séquence vide est False.

if liste: # La liste n'est pas vide

Enfin, ne comparez pas des booléens à True ou False :

# Oui
    if booleen: # Si booleen est vrai
        ...
    if not booleen: # Si booleen n'est pas vrai
        ...

# Non
    if booleen == True:
        ...

# Encore pire
    if booleen is True:
        ...

Conclusion

Voilà pour la PEP 8 ! Elle contient beaucoup de conventions et toutes ne figurent pas dans cette section. Celles que j'ai présentées ici, dans tous les cas, sont moins détaillées. Je vous invite donc à faire un tour du côté du texte original si vous désirez en savoir plus.

La PEP 257 : de belles documentations

Nous allons nous intéresser à présent à la PEP 257 qui définit d'autres conventions concernant la documentation via les docstrings. Consultez-la sur le site de Python.

def fonction(parametre1, parametre2):
    """Documentation de la fonction."""

La ligne 2 de ce code, que vous avez sans doute reconnue, est une docstring. Nous allons voir quelques conventions autour de l'écriture de ces docstrings (comment les rédiger, qu'y faire figurer, etc.).

Une fois de plus, je vais prendre quelques libertés avec le texte original de la PEP. Je ne vous proposerai pas une traduction complète de la PEP mais je reviendrai sur les points que je considère importants.

Qu'est-ce qu'une docstring ?

La docstring (chaîne de documentation, en français) est une chaîne de caractères placée juste après la définition d'un module, d'une classe, fonction ou méthode. Cette chaîne de caractères devient l'attribut spécial __doc__ de l'objet.

>>> fonction.__doc__
'Documentation de la fonction.'
>>>

Tous les modules doivent être documentés grâce aux docstrings. Les fonctions et classes exportées par un module doivent également être documentées ainsi. Cela vaut aussi pour les méthodes publiques d'une classe (y compris le constructeur __init__). Un package peut être documenté via une docstring placée dans le fichier __init__.py.

Pour des raisons de cohérence, utilisez toujours des guillemets triples """ autour de vos docstrings. Utilisez """chaîne de documentation""" si votre chaîne comporte des anti-slash \.

On peut trouver les docstrings sous deux formes :

  • sur une seule ligne ;

  • sur plusieurs lignes.

Les docstrings sur une seule ligne

def kos_root():
    """Return the pathname of the KOS root directory."""
    global _kos_root
    if _kos_root: return _kos_root
    …
Notes
  • Les guillemets triples sont utilisés même si la chaîne tient sur une seule ligne. Il est plus simple de l'étendre par la suite dans ces conditions.

  • Les trois guillemets """ fermant la chaîne sont sur la même ligne que les trois guillemets qui l'ouvrent. Ceci est préférable pour une docstring d'une seule ligne.

  • Il n'y a aucun saut de ligne avant ou après la docstring.

  • La chaîne de documentation est une phrase, elle se termine par un point ..

  • La docstring sur une seule ligne ne doit pas décrire la signature des paramètres à passer à la fonction/méthode, ou son type de retour. N'écrivez pas :

def fonction(a, b):
    """fonction(a, b) -> list"""

Cette syntaxe est uniquement valable pour les fonctions C (comme les built-ins). Pour les fonctions Python, l'introspection peut être utilisée pour déterminer les paramètres attendus. L'introspection ne peut cependant pas être utilisée pour déterminer le type de retour de la fonction/méthode. Si vous voulez le préciser, incluez-le dans la docstring sous une forme explicite :

"""Fonction faisant cela et renvoyant une liste."""

Bien entendu, « faisant cela » doit être remplacé par une description utile de ce que fait la fonction !

Les docstrings sur plusieurs lignes

Les docstrings sur plusieurs lignes sont constituées d'une première ligne résumant brièvement l'objet (fonction, méthode, classe, module), suivie d'un saut de ligne, suivi d'une description plus longue. Respectez autant que faire se peut cette convention : une ligne de description brève, un saut de ligne puis une description plus longue.

La première ligne de la docstring peut se trouver juste après les guillemets ouvrant la chaîne ou juste en-dessous.
Dans tous les cas, le reste de la docstring doit être indenté au même niveau que la première ligne :

class MaClasse:
    def __init__(self, ...):
        """Constructeur de la classe MaClasse

        Une description plus longue...
        sur plusieurs lignes...
        
        """

Insérez un saut de ligne avant et après chaque docstring documentant une classe.

La docstring d'un module doit généralement dresser la liste des classes, exceptions et fonctions, ainsi que des autres objets exportés par ce module (une ligne de description par objet). Cette ligne de description donne généralement moins d'informations sur l'objet que sa propre documentation. La documentation d'un package (la docstring se trouvant dans le fichier __init__.py) doit également dresser la liste des modules et sous-packages qu'il exporte.

La documentation d'une fonction ou méthode doit décrire son comportement et documenter ses arguments, sa valeur de retour, ses effets de bord, les exceptions qu'elle peut lever et les restrictions concernant son appel (quand ou dans quelles conditions appeler cette fonction). Les paramètres optionnels doivent également être documentés.

def complexe(reel=0.0, image=0.0):
    """Forme un nombre complexe.

    Paramètres nommés :
    reel -- la partie réelle (0.0 par défaut)
    image -- la partie imaginaire (0.0 par défaut)

    """
    if image == 0.0 and reel == 0.0: return complexe_zero
    ...

La documentation d'une classe doit, de même, décrire son comportement, documenter ses méthodes publiques et ses attributs.

Le BDFL nous conseille de sauter une ligne avant de fermer nos docstrings quand elles sont sur plusieurs lignes. Les trois guillemets fermant la docstring sont ainsi sur une ligne vide par ailleurs.

def fonction():
    """Documentation brève sur une ligne.
    
    Documentation plus longue...
    
    """
Example of certificate of achievement
Example of certificate of achievement