15 Bonnes pratiques en programmation Python
Comme vous l'avez constaté dans tous les chapitres précédents, la syntaxe de Python est très permissive. Afin d'uniformiser l'écriture de code en Python, la communauté des développeurs Python recommande un certain nombre de règles afin qu'un code soit lisible. Lisible par quelqu'un d'autre, mais également, et surtout, par soi-même. Essayez de relire un code que vous avez écrit « rapidement » il y a un 1 mois, 6 mois ou un an. Si le code ne fait que quelques lignes, il se peut que vous vous y retrouviez, mais s'il fait plusieurs dizaines voire centaines de lignes, vous serez perdus.
Dans ce contexte, le créateur de Python, Guido van Rossum, part d'un constat simple : « code is read much more often than it is written » (« le code est plus souvent lu qu'écrit »). Avec l'expérience, vous vous rendrez compte que cela est parfaitement vrai. Alors plus de temps à perdre, voyons en quoi consistent ces bonnes pratiques.
Plusieurs choses sont nécessaires pour écrire un code lisible : la syntaxe, l'organisation du code, le découpage en fonctions (et possiblement en classes que nous verrons dans le chapitre 19 Avoir la classe avec les objets), mais souvent, aussi, le bon sens. Pour cela, les « PEP » peuvent nous aider.
Afin d'améliorer le langage Python, la communauté qui développe Python publie régulièrement des Python Enhancement Proposal (PEP), suivi d'un numéro. Il s'agit de propositions concrètes pour améliorer le code, ajouter de nouvelles fonctionnalités, mais aussi des recommandations sur la manière d'utiliser Python, bien écrire du code, etc.
On va aborder dans ce chapitre sans doute la plus célèbre des PEP, à savoir la PEP 8, qui est incontournable lorsque l'on veut écrire du code Python correctement.
On parle de code pythonique lorsque ce dernier respecte les règles d'écriture définies par la communauté Python mais aussi les règles d'usage du langage.
15.1 De la bonne syntaxe avec la PEP 8
La PEP 8 Style Guide for Python Code est une des plus anciennes PEP (les numéros sont croissants avec le temps). Elle consiste en un nombre important de recommandations sur la syntaxe de Python. Il est vivement recommandé de lire la PEP 8 en entier au moins une fois pour avoir une bonne vue d'ensemble. On ne présentera ici qu'un rapide résumé de cette PEP 8.
15.1.1 Indentation
On a vu que l'indentation est obligatoire en Python pour séparer les blocs d'instructions. Cela vient d'un constat simple, l'indentation améliore la lisibilité d'un code. Dans la PEP 8, la recommandation pour la syntaxe de chaque niveau d'indentation est très simple : 4 espaces. N'utilisez pas autre chose, c'est le meilleur compromis.
Afin de toujours utiliser cette règle des 4 espaces pour l'indentation, il est essentiel de régler correctement votre éditeur de texte. Consultez pour cela l'annexe Installation de Python disponible en ligne. Avant d'écrire la moindre ligne de code, faites en sorte que lorsque vous pressez la touche tabulation, cela ajoute 4 espaces (et non pas un caractère tabulation).
15.1.2 Importation des modules
Comme on l'a vu au chapitre 8 Modules, le chargement d'un module se fait avec l'instruction import module
plutôt qu'avec from module import *
.
Si on souhaite ensuite utiliser une fonction d'un module, la première syntaxe conduit à module.fonction()
ce qui rend explicite la provenance de la fonction. Avec la seconde syntaxe, il faudrait écrire fonction()
ce qui peut :
- mener à un conflit si une de vos fonctions a le même nom ;
- rendre difficile la recherche de documentation si on ne sait pas d'où vient la fonction, notamment si plusieurs modules sont chargés avec l'instruction
from module import *
Par ailleurs, la première syntaxe définit un « espace de noms » (voir chapitre 19 Avoir la classe avec les objets) spécifique au module.
Dans un script Python, on importe en général un module par ligne. D'abord les modules internes (classés par ordre alphabétique), c'est-à-dire les modules de base de Python, puis les modules externes (ceux que vous avez installés en plus).
Si le nom du module est trop long, on peut utiliser un alias. L'instruction from
est tolérée si vous n'importez que quelques fonctions clairement identifiées.
En résumé :
1 2 3 4 5 6 7 8 |
|
15.1.3 Règles de nommage
Les noms de variables, de fonctions et de modules doivent être de la forme :
1 2 3 |
|
c'est-à-dire en minuscules avec un caractère « souligné » (« tiret du bas » ou underscore en anglais) pour séparer les différents « mots » dans le nom.
Les constantes sont écrites en majuscules :
1 2 |
|
Les noms de classes (chapitre 19) et les exceptions (chapitre 21) sont de la forme :
1 2 |
|
Le style recommandé pour nommer les variables et les fonctions en Python est appelé snake_case. Il est différent du CamelCase utilisé pour les noms des classes et des exceptions.
Pensez à donner à vos variables des noms qui ont du sens. Évitez autant que possible les a1
, a2
, i
, truc
, toto
... Les noms de variables à un caractère sont néanmoins autorisés pour les boucles et les indices :
1 2 3 |
|
Bien sûr, une écriture plus « pythonique » de l'exemple précédent permet de se débarrasser de l'indice i
:
1 2 3 4 |
|
Enfin, des noms de variable à une lettre peuvent être utilisés lorsque cela a un sens mathématique (par exemple, les noms x
, y
et z
évoquent des coordonnées cartésiennes).
15.1.4 Gestion des espaces
La PEP 8 recommande d'entourer les opérateurs (+
, -
, /
, *
, ==
, !=
, >=
, not
, in
, and
, or
...) d'un espace avant et d'un espace après. Par exemple :
1 2 3 4 5 6 7 8 |
|
Il n'y a, par contre, pas d'espace à l'intérieur de crochets, d'accolades et de parenthèses :
1 2 3 4 5 6 7 8 |
|
Ni juste avant la parenthèse ouvrante d'une fonction ou le crochet ouvrant d'une liste ou d'un dictionnaire :
1 2 3 4 5 6 7 8 |
|
On met un espace après les caractères :
et ,
(mais pas avant) :
1 2 3 4 5 6 7 8 |
|
Par contre, pour les tranches de listes, on ne met pas d'espace autour du :
1 2 3 4 5 6 7 8 9 |
|
Enfin, on n'ajoute pas plusieurs espaces autour du =
ou des autres opérateurs pour faire joli :
1 2 3 4 5 6 7 8 |
|
15.1.5 Longueur de ligne
Une ligne de code ne doit pas dépasser 79 caractères, pour des raisons tant historiques que de lisibilité.
On a déjà vu au chapitre 1 Introduction que le caractère \
permet de couper des lignes trop longues. Par exemple :
1 2 3 4 5 6 |
|
À l'intérieur d'une parenthèse, on peut revenir à la ligne sans utiliser le caractère \
. C'est particulièrement utile pour préciser les arguments d'une fonction ou d'une méthode, lors de sa création ou lors de son utilisation :
1 2 3 4 5 6 7 |
|
Les parenthèses sont également très pratiques pour répartir sur plusieurs lignes une chaîne de caractères qui sera affichée sur une seule ligne :
1 2 3 4 |
|
Notez qu'il n'y a pas d'opérateur +
pour concaténer les trois chaînes de caractères et que celles-ci ne sont pas séparées par des virgules. À partir du moment où elles sont entre parenthèses, Python les concatène automatiquement.
On peut aussi utiliser les parenthèses pour évaluer un expression trop longue :
1 2 3 4 5 6 |
|
Les parenthèses sont aussi très utiles lorsqu'on a besoin d’enchaîner des méthodes les unes à la suite des autres. Un exemple se trouve dans le chapitre 17 Quelques modules d'intérêt en bioinformatique, dans la partie consacrée au module pandas.
Enfin, il est possible de créer des listes ou des dictionnaires sur plusieurs lignes, en sautant une ligne après une virgule :
1 2 3 4 5 6 |
|
15.1.6 Lignes vides
Dans un script, les lignes vides sont utiles pour séparer visuellement les différentes parties du code.
Il est recommandé de laisser deux lignes vides avant la définition d'une fonction ou d'une classe et de laisser une seule ligne vide avant la définition d'une méthode (dans une classe).
On peut aussi laisser une ligne vide dans le corps d'une fonction pour séparer les sections logiques de la fonction, mais cela est à utiliser avec parcimonie.
15.1.7 Commentaires
Les commentaires débutent toujours par le symbole #
suivi d'un espace. Ils donnent des explications claires sur l'utilité du code et doivent être synchronisés avec le code, c'est-à-dire que si le code est modifié, les commentaires doivent l'être aussi (le cas échéant).
Les commentaires sont sur le même niveau d'indentation que le code qu'ils commentent. Les commentaires sont constitués de phrases complètes, avec une majuscule au début (sauf si le premier mot est une variable qui s'écrit sans majuscule) et un point à la fin.
La PEP 8 recommande très fortement d'écrire les commentaires en anglais, sauf si vous êtes à 120% sûr que votre code ne sera lu que par des francophones. Dans la mesure où vous allez souvent développer des programmes scientifiques, nous vous conseillons d'écrire vos commentaires en anglais.
Soyez également cohérent entre la langue utilisée pour les commentaires et la langue utilisée pour nommer les variables. Pour un programme scientifique, les commentaires et les noms de variables sont en anglais. Ainsi ma_liste
deviendra my_list
et ma_fonction
deviendra my_function
(par exemple).
Les commentaires qui suivent le code sur la même ligne sont à éviter le plus possible et doivent être séparés du code par au moins deux espaces :
1 |
|
Nous terminerons par une remarque qui concerne la syntaxe, mais qui n'est pas incluse dans la PEP 8. On nous pose souvent la question du type de guillemets à utiliser pour déclarer une chaîne de caractères. Simples ou doubles ?
1 2 3 4 5 6 7 8 |
|
Vous constatez dans l'exemple ci-dessus que pour Python, c'est exactement la même chose. Et à notre connaissance, il n'existe pas de recommandation officielle sur le sujet.
Nous vous conseillons cependant d'utiliser les guillemets doubles car ceux-ci sont, de notre point de vue, plus lisibles.
15.2 Les docstrings et la PEP 257
Les docstrings, que l'on pourrait traduire par « chaînes de documentation » en français, sont un élément essentiel de nos programmes Python comme on l'a vu au chapitre 14 Création de modules. À nouveau, les développeurs de Python ont émis des recommandations dans la PEP 8 et plus exhaustivement dans la PEP 257 sur la manière de rédiger correctement les docstrings. En voici un résumé succinct.
De manière générale, écrivez des docstrings pour les modules, les fonctions, les classes et les méthodes. Lorsque l'explication est courte et compacte comme dans certaines fonctions ou méthodes simples, utilisez des docstrings d'une ligne :
1 |
|
Lorsque vous avez besoin de décrire plus en détail un module, une fonction, une classe ou une méthode, utilisez une docstring sur plusieurs lignes.
1 2 3 4 5 6 7 8 9 |
|
La PEP 257 recommande d'écrire des docstrings avec des triples doubles guillemets, c'est-à-dire
"""Ceci est une docstring recommandée."""
mais pas
'''Ceci n'est pas une docstring recommandée.'''
.
Comme indiqué dans le chapitre 14 Création de modules, n'oubliez pas que les docstrings sont destinées aux utilisateurs des modules, fonctions, méthodes et classes que vous avez développés. Les éléments essentiels pour les fonctions et les méthodes sont :
- ce que fait la fonction ou la méthode,
- ce qu'elle prend en argument,
- ce qu'elle renvoie.
Pour les modules et les classes, on ajoute également des informations générales sur leur fonctionnement.
Pour autant, la PEP 257 ne dit pas explicitement comment organiser les docstrings pour les fonctions et les méthodes. Pour répondre à ce besoin, deux solutions ont émergées :
- La solution Google avec le Google Style Python Docstrings.
- La solution NumPy avec le NumPy Style Python Docstrings. NumPy qui est un module complémentaire à Python, très utilisé en analyse de données et dont on parlera dans le chapitre 17 Quelques modules d'intérêt en bioinformatique.
On illustre ici la solution NumPy pour des raisons de goût personnel. Sentez-vous libre d'aller explorer la proposition de Google. Voici un exemple très simple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Lignes 6 et 7. La section Parameters
précise les paramètres de la fonction. Les tirets sur la ligne 7 permettent de souligner le nom de la section et donc de la rendre visible.
Lignes 8 et 9. On indique le nom et le type du paramètre séparés par le caractère deux-points. Le type n'est pas obligatoire. En dessous, on indique une description du paramètre en question. La description est indentée.
Lignes 10 à 14. Même chose pour le second paramètre. La description du paramètre peut s'étaler sur plusieurs lignes.
Lignes 16 et 17. La section Returns
indique ce qui est renvoyé par la fonction (le cas échéant).
Lignes 18 et 19. La mention du type renvoyé est obligatoire. En dessous, on indique une description de ce qui est renvoyé par la fonction. Cette description est aussi indentée.
L'être humain a une fâcheuse tendance à la procrastination (le fameux « Bah je le ferai demain...») et écrire de la documentation peut être un sérieux motif de procrastination. Soyez vigilant sur ce point, et rédigez vos docstrings au moment où vous écrivez vos modules, fonctions, classes ou méthodes. Passer une journée (voire plusieurs) à écrire les docstrings d'un gros projet est particulièrement pénible. Croyez-nous !
15.3 Outils de contrôle qualité du code
Pour évaluer la qualité d'un code Python, c'est-à-dire sa conformité avec les recommandations de la PEP 8 et de la PEP 257, on peut utiliser des sites internet ou des outils dédiés.
Le site pep8online, par exemple, est très simple d'utilisation. On copie / colle le code à évaluer puis on clique sur le bouton Check code.
Les outils pycodestyle
, pydocstyle
et pylint
doivent par contre être installés sur votre machine. Avec la distribution Miniconda, cette étape d'installation se résume à une ligne de commande :
1 |
|
Les outils pycodestyle
, pydocstyle
et pylint
sont des linters, c'est-à-dire des programmes qui vont chercher les sources potentielles d'erreurs dans un code informatique. Ces erreurs peuvent être des erreurs de style (PEP 8 et 257) ou des erreurs logiques (manipulation d'une variable, chargement de module).
Voici le contenu du script script_quality_not_ok.py
que nous allons analyser par la suite :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Ce script est d'ailleurs parfaitement fonctionnel :
1 2 3 |
|
On va tout d'abord vérifier la conformité avec la PEP 8 avec l'outil pycodestyle
:
1 2 3 4 5 6 |
|
Ligne 2. Le bloc script_quality_not_ok.py:6:1:
désigne le nom du script (script_quality_not_ok.py
), le numéro de la ligne (6) et le numéro de la colonne (1) où se trouve la non-conformité avec la PEP 8. Ensuite, pycodestyle
fournit un code et un message explicatif. Ici, il faut deux lignes vides avant la fonction Multiplie_nombres()
.
Ligne 3. Il manque un espace après la virgule qui sépare les arguments nombre1
et nombre2
dans la définition de la fonction Multiplie_nombres()
à la ligne 6 (colonne 30) du script.
Ligne 4. Il y un espace de trop après le second argument nombre2
dans la définition de la fonction Multiplie_nombres()
à la ligne 6 (colonne 38) du script.
Ligne 5. Il manque un espace après l'opérateur *
à la ligne 26 (colonne 21) du script.
Ligne 6. Il y a un espace de trop entre print
et (
à la ligne 31 (colonne 10) du script.
Remarquez que curieusement, pycodestyle
n'a pas détecté que le nom de la fonction Multiplie_nombres()
ne respecte pas la convention de nommage.
Ensuite, l'outil pydocstyle
va vérifier la conformité avec la PEP 257 et s'intéresser particulièrement aux docstrings :
1 2 3 4 5 6 7 |
|
Lignes 2 et 3. pydocstyle
indique que la docstring à la ligne 1 du script est sur deux lignes alors qu'elle devrait être sur une seule ligne.
Lignes 4 et 5. Dans la docstring de la fonction Multiplie_nombres()
(ligne 7 du script), il manque une ligne vide entre la ligne résumé et la description plus complète.
Lignes 6 et 7. Dans la docstring de la fonction Multiplie_nombres()
(ligne 7 du script), il manque un point à la fin de la première ligne.
Les outils pycodestyle
et pydocstyle
vont simplement vérifier la conformité aux PEP 8 et 257. L'outil pylint
va lui aussi vérifier une partie de ces règles mais il va également essayer de comprendre le contexte du code et proposer des éléments d'amélioration. Par exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Lignes 3 à 5. pylint
indique qu'il manque un espace entre les paramètres de la fonction Multiplie_nombres()
(ligne 6 et colonne 29 du script). La ligne du script qui pose problème est affichée, ce qui est pratique.
Lignes 6 à 8. pylint
identifie un espace de trop après le second paramètre de la fonction Multiplie_nombres()
.
Ligne 9 à 11. Il y a un espace de trop entre print
et (
.
Lignes 12 et 13. Le nom de la fonction Multiplie_nombres()
ne respecte pas la convention PEP 8. La fonction devrait s'appeler multiplie_nombres()
.
Ligne 14. Le module os
est chargé mais pas utilisé (ligne 4 du script).
Ligne 17. pylint
produit également une note sur 10. Ne soyez pas surpris si cette note est très basse (voire négative) la première fois que vous analysez votre script avec pylint
. Cet outil fournit de nombreuses suggestions d'amélioration et la note attribuée à votre script devrait rapidement augmenter. Pour autant, la note de 10 est parfois difficile à obtenir. Ne soyez pas trop exigeant.
Une version améliorée du script précédent est disponible en ligne.
15.4 Organisation du code
Il est fondamental de toujours structurer et organiser son code de la même manière. Ainsi, on sait tout de suite où trouver l'information et un autre programmeur pourra s'y retrouver. Voici un exemple de code avec les différents éléments dans le bon ordre :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
|
Lignes 1 à 9. Cette docstring décrit globalement le script. Cette docstring (ainsi que les autres) seront visibles si on importe le script en tant que module, puis en invoquant la commande help()
(voir chapitre 14 Création de modules).
Lignes 11 à 15. On définit ici un certain nombres de variables avec des doubles underscores donnant quelques informations sur la version du script, les auteurs, etc. Il s'agit de métadonnées que la commande help()
pourra afficher. Bien sûr, ces métadonnées ne sont pas obligatoires, mais elles sont utiles lorsque le code est distribué à la communauté.
Lignes 17 à 20. Importation des modules. D'abord les modules internes à Python (fournis en standard), puis les modules externes (ceux qu'il faut installer en plus), un module par ligne.
Lignes 22 et 23. Définition des constantes. Le nom des constantes est en majuscule.
Ligne 26. Définition d'une classe. On a laissé deux lignes vides avant.
Lignes 27 à 32. Docstring décrivant la classe.
Lignes 33, 42 et 46. Avant chaque méthode de la classe, on laisse une ligne vide.
Lignes 58 à 72. Après les classes, on met les fonctions « classiques ». Avant chaque fonction, on laisse deux lignes vides.
Lignes 75 à 77. On écrit le programme principal. Le test ligne 76 n'est vrai que si le script est utilisé en tant que programme. Les lignes suivantes ne sont donc pas exécutées si le script est chargé comme un module.
15.5 Conseils sur la conception d'un script
Voici quelques conseils pour vous aider à concevoir un script Python.
- Réfléchissez avec un papier, un crayon... et un cerveau (voire même plusieurs) ! Reformulez avec des mots en français (ou en anglais) les consignes qui vous ont été données ou le cahier des charges qui vous a été communiqué. Dessinez ou construisez des schémas si cela vous aide.
- Découpez en fonctions chaque élément de votre programme. Vous pourrez ainsi tester chaque élément indépendamment du reste. Pensez à écrire les docstrings en même temps que vous écrivez vos fonctions.
- Quand l'algorithme est complexe, commentez votre code pour expliquer votre raisonnement. Utiliser des fonctions (ou méthodes) encore plus petites peut aussi être une solution.
- Documentez-vous. L'algorithme dont vous avez besoin existe-t-il déjà dans un autre module ? Existe-t-il sous la forme de pseudo-code ? De quels outils mathématiques avez-vous besoin dans votre algorithme ?
- Si vous créez ou manipulez une entité cohérente avec des propriétés propres, essayez de construire une classe. Jetez, pour cela, un œil au chapitre 19 Avoir la classe avec les objets.
- Utilisez des noms de variables explicites, qui signifient quelque chose. En lisant votre code, on doit comprendre ce que vous faites. Choisir des noms de variables pertinents permet aussi de réduire les commentaires.
- Quand vous construisez une structure de données complexe (par exemple une liste de dictionnaires contenant d'autres objets), documentez et illustrez l'organisation de cette structure de données sur un exemple simple.
- Testez toujours votre code sur un jeu de données simple pour pouvoir comprendre rapidement ce qui se passe. Par exemple, une séquence de 1000 bases est plus facile à gérer que le génome humain ! Cela vous permettra également de retrouver plus facilement une erreur lorsque votre programme ne fait pas ce que vous souhaitez.
- Lorsque votre programme « plante », lisez le message d'erreur. Python tente de vous expliquer ce qui ne va pas. Le numéro de la ligne qui pose problème est aussi indiqué.
- Discutez avec des gens. Faites tester votre programme par d'autres. Les instructions d'utilisation sont-elles claires ?
- Si vous distribuez votre code :
- Rédigez une documentation claire.
- Testez votre programme (jetez un œil aux tests unitaires).
- Précisez une licence d'utilisation. Voir par exemple le site Choose an open source license.
15.6 Pour terminer : la PEP 20
La PEP 20 est une sorte de réflexion philosophique avec des phrases simples qui devraient guider tout programmeur. Comme les développeurs de Python ne manque pas d'humour, celle-ci est accessible sous la forme d'un « œuf de Pâques » (easter egg en anglais) ou encore « fonctionnalité cachée d'un programme » en important un module nommé this
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Et si l'aventure et les easter eggs vous plaisent, testez également la commande
1 |
|
Il vous faudra un navigateur et une connexion internet.
-
L'article Python Code Quality: Tools & Best Practices du site Real Python est une ressource intéressante pour explorer plus en détail la notion de qualité pour un code Python. De nombreux linters y sont présentés.
-
Les articles Assimilez les bonnes pratiques de la PEP 8 du site OpenClassrooms et Structuring Python Programs du site Real Python rappellent les règles d'écriture et les bonnes pratiques vues dans ce chapitre.