Améliorer la qualité de son code

Présentation des standards permettant de produire du code lisible et maintenable, et d’outils pour faciliter leur adoption.

Author

Romain Avouac et Lino Galiana

Dérouler les slides ci-dessous ou cliquer ici pour afficher les slides en plein écran.

Introduction

L’enjeu d’un code lisible et maintenable

“The code is read much more often than it is written.”

Guido Van Rossum1

Lorsque l’on s’initie à la pratique de la data science, il est assez naturel de voir le code d’une manière très fonctionnelle : je veux réaliser une tâche donnée — par exemple un algorithme de classification — et je vais donc assembler dans un notebook des bouts de code, souvent trouvés sur internet, jusqu’à obtenir un projet qui réalise la tâche voulue. La structure du projet importe assez peu, tant qu’elle permet d’importer correctement les données nécessaires à la tâche en question.

Si cette approche flexible et minimaliste fonctionne très bien lors de la phase d’apprentissage, il est malgré tout indispensable de s’en détacher progressivement à mesure que l’on progresse et que l’on peut être amené à réaliser des projets plus professionnels ou bien à intégrer des projets collaboratifs.

En particulier, il est important de proposer, parmi les multiples manières de résoudre un problème informatique, une solution qui soit intelligible par d’autres personnes parlant le langage. Le code est en effet lu bien plus souvent qu’il n’est écrit, c’est donc avant tout un outil de communication. De même, la maintenance d’un code demande généralement beaucoup plus de moyens que sa phase de développement initial, il est donc important de penser en amont la qualité de son code et la structure de son projet de sorte à le rendre au maximum maintenable dans le temps.

Afin de faciliter la communication et réduire la douleur d’avoir à faire évoluer un code obscur, des tentatives plus ou moins institutionnalisées de définir des conventions ont émergé. Ces conventions dépendent naturellement du langage utilisé, mais les principes sous-jacents s’appliquent de manière universelle à tout projet basé sur du code.

De l’importance de suivre les conventions

Python est un langage très lisible. Avec un peu d’effort sur le nom des objets, sur la gestion des dépendances et sur la structure du programme, on peut très bien comprendre un script sans avoir besoin de l’exécuter. C’est l’une des principales forces du langage Python qui permet ainsi une acquisition rapide des bases et facilite l’appropriation d’un script.

La communauté Python a abouti à un certain nombre de normes, dites PEP (Python Enhancement Proposal), qui constituent un standard dans l’écosystème Python. Les deux normes les plus connues sont :

  • la norme PEP8 qui définit un certain nombre de conventions relatives au code
  • la norme PEP257 consacrée à la documentation (docstrings).
Note

Dans l’univers R, la formalisation a été moins organisée. Ce langage est plus permissif que Python sur certains aspects2. Néanmoins, des standards ont émergé, à travers un certain nombre de style guides dont les plus connus sont le tidyverse style guide et le google style guide3 (voir ce post qui pointe vers un certain nombre de ressources sur le sujet).

Ces conventions sont arbitraires, dans une certaine mesure. Il est tout à fait possible de trouver certaines conventions moins esthétiques que d’autres.

Ces conventions ne sont pas non plus immuables: les langages et leurs usages évoluent, ce qui nécessite de mettre à jour les conventions. Cependant, adopter dans la mesure du possible certains des réflexes préconisés par ces conventions devrait améliorer la capacité à être compris par la communauté, augmenter les chances de bénéficier d’apport de celle-ci pour adapter le code mais aussi réduire la difficulté à faire évoluer un code.

Il existe beaucoup de philosophies différentes sur le style de codage et, en fait, le plus important est la cohérence : si on choisit une convention, par exemple snake case plutôt que camel case, le mieux est de s’y tenir.

Les conventions vont au-delà de la syntaxe. Un certain nombre de standards d’organisation d’un projet ont émergé, qui seront abordées dans le prochain chapitre.

Comment adopter ces bonnes pratiques ?

Les éléments exposés dans ce chapitre ne sont pas exhaustifs. Ils visent à pointer vers quelques problématiques prioritaires tout en proposant des conseils pratiques. L’équivalent R de ce cours est proposé sous la forme de formation à l’Insee et est accessible depuis ce lien.

Dans la lignée de la vision des bonnes pratiques comme continuum proposée en introduction, il n’est pas nécessairement souhaitable d’appliquer toutes les recommendations présentées dans ce chapitre à chaque projet. Nous recommandons de les voir plutôt comme des bonnes habitudes à acquérir en opérant un va-et-vient régulier entre la pratique et la théorie. Les outils que nous allons proposer seront là pour accélérer la mise en oeuvre des bonnes pratiques.

Il est également assez instructif de regarder le code modifié par les outils pour comprendre et corriger certains problèmes dans sa manière de développer. Par exemple, à la lecture de ce chapitre, vous allez certainement retenir en particulier certaines règles qui tranchent avec vos pratiques actuelles. Vous pouvez alors essayer d’appliquer ces nouvelles règles pendant un certain temps puis, lorsque celles-ci seront devenues naturelles, revenir à ce guide et appliquer le processus à nouveau. En procédant ainsi de manière incrémentale, vous améliorerez progressivement la qualité de vos projets sans avoir l’impression de passer trop de temps sur des micro-détails, au détriment des objectifs globaux du projet.

Principes généraux

Les premières conventions à évoquer ont trait à la syntaxe du code et ont les objectifs suivants, qui seront détaillés par la suite :

Lisibilité

Un code écrit avec des noms de variables et de fonctions explicites est autant, voire plus, informatif que les commentaires qui l’accompagnent4. C’est pourquoi il est essentiel de respecter des conventions pour le choix des noms des objets afin d’assurer la lisibilité des programmes.

Un certain nombre de conseils sont présents dans le Hitchhiker’s Guide to Python qui vise à faire connaître les préceptes du “Zen of Python” (PEP 20). Ce post de blog illustre quelques uns de ces principes avec des exemples. Vous pouvez retrouver ces conseils dans Python en tapant le code suivant:

import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Il est conseillé de suivre ces deux principes lorsqu’on commence à programmer des fonctions (ce qui, comme cela est évoqué par la suite, est toujours recommandé).

  • Faire attention au type d’objet renvoyé par Python. Ce langage ne propose pas de typage fort, il est donc possible qu’une fonction renvoie des objets de nature différente selon les cas5. Cela peut amener à des surprises lorsqu’on utilise une telle fonction dans un code. Il est recommandé d’éviter ce comportement en proposant des fonctions différentes si l’output d’une fonction est de nature différente. Ce principe de précaution (mais aussi d’information) renvoie au paradigme de la programmation défensive.
  • Privilégier la programmation orientée objet lorsqu’une fonction doit s’adapter au type d’objet en entrée (par exemple aller chercher des éléments différents pour un objet lm ou un objet glm). Cela évite les codes spaghetti 🍝 inutilement complexes qui sont impossibles à débugger.
Type hinting

Python propose une fonctionalité assez plaisante qui est le type hinting (doc officielle et tutoriel sur realpython.com).

Cette approche permet d’indiquer le type d’argument attendu par une fonction et celui qui sera renvoyé par la fonction. Par exemple, la personne ayant écrit la fonction suivante

def calcul_moyenne(df: pd.DataFrame, col : str = "y") -> pd.DataFrame:
    return df[col].mean()

propose d’utiliser deux types d’inputs (un DataFrame Pandas et une chaine de caractère) et indique qu’elle renverra un DataFrame Pandas. A noter que c’est indicatif, non contraignant. En effet, le code ci-dessus fonctionnera si on fournit en argument col une liste puisque Pandas sait gérer cela à l’étape df[col].mean().

Le type hinting est un élément d’autodocumentation puisque grâce à ces hints le code suffit à faire comprendre la volonté de la personne l’ayant écrit.

Le code spaghetti

Le code spaghetti est un style d’écriture qui favorise l’apparition du syndrome du plat de spaghettis : un code impossible à déméler parce qu’il fait un usage excessif de conditions, d’exceptions en tous sens, de gestion des événements complexes. Il devient quasi-impossible de savoir quelles ont été les conditions à l’origine de telle ou telle erreur sans exécuter ligne à ligne (et celles-ci sont excessivement nombreuses du fait de mauvaises pratiques de programmation) le programme.

En fait, la programmation spaghetti qualifie tout ce qui ne permet pas de déterminer le qui, le quoi et le comment. Le code est donc plus long à mettre à jour car cela nécessite de remonter un à un le fil des renvois.

Concision

Un code reproductible doit pouvoir s’exécuter de manière linéaire. S’il provoque une erreur, il est important de pouvoir identifier l’instruction responsable pour pouvoir debugger. Comme une démonstration mathématique, un code intelligible doit viser la concision et la simplicité. Les codes très longs sont souvent signes de répétitions et sont difficiles à débugger.

Les scripts trop longs ne sont pas une bonne pratique. Il est préférable de diviser l’ensemble des scripts exécutant une chaîne de production en “monades”, c’est-à-dire en petites unités cohérentes. Les fonctions sont un outil privilégié pour cela (en plus de limiter la redondance, et d’être un outil privilégié pour documenter un code).

En Python, il est recommandé de privilégier les list comprehensions à l’utilisation de boucles for indentées. Ces dernières sont en général moins efficaces et surtout impliquent un nombre important de ligne de codes là où les compréhensions de listes sont beaucoup plus concises

liste_nombres = range(10)

# très mauvais
y = []
for x in liste_nombres:
    if x % 2 == 0:
        y.append(x*x)

# mieux
y = [x*x for x in liste_nombres if x % 2 == 0]
Règle d’or

Il faut utiliser une fonction dès qu’on utilise une même portion de code plus de deux fois (don’t repeat yourself (DRY))

Les fonctions ont de nombreux avantages par rapport à de longs scripts:

  • Limite les risques d’erreurs liés aux copier/coller
  • Rend le code plus lisible et plus compact
  • Un seul endroit du code à modifier lorsqu’on souhaite modifier le traitement
  • Facilite la réutilisation et la documentation du code !
Règles pour écrire des fonctions pertinentes
  • Une tâche = une fonction
  • Une tâche complexe = un enchaînement de fonctions réalisant chacune une tâche simple
  • Limiter l’utilisation de variables globales.

Cohérence du script

Lister les dépendances est important:

  • pour des raisons techniques: le logiciel doit savoir où aller chercher les fonctions utilisées dans un script pour avoir un code fonctionnel ;
  • pour des raisons conventionnelles: les utilisateurs doivent comprendre les dépendances à installer pour être en mesure de réutiliser le code.

Les imports se mettent conventionnellement en début de script, qu’il s’agisse d’import de packages dans leur ensemble ou seulement de certaines fonctions:

import pandas as pd
from sklearn.model_selection import cross_val_score

Dans le premier cas, on fait ensuite référence aux fonctions en les faisant précéder du nom du package :

pd.DataFrame([0,1])

Cela permet de dire à Python d’aller chercher dans le namespace pd (alias pour pandas qui est lui-même un ensemble de scripts enregistrés sur le disque) la fonction DataFrame.

En premier lieu, il convient d’adopter les mêmes standards que la communauté pour les noms de package.

# bien
import numpy as np

# trompeur
import numpy as pd

Il faut également faire attention aux namespaces pour éviter les conflits entre fonctions. Cela implique de ne pas importer l’ensemble des fonctions d’un package de la manière suivante:

from numpy import *
from math import *

Dans ce cas, on va se retrouver avec des conflits potentiels entre les fonctions du package numpy et du package math qui portent le même nom (floor par exemple).

En ce qui concerne l’installation des packages, nous allons voir dans les parties Structure de code et Portabilité qu’il ne faut pas gérer ceci dans le script mais dans un élément à part, relatif à l’environnement d’exécution du projet6.

Limiter la redondance

Un bon principe à suivre est “don’t repeat yourself !” (DRY). Celui-ci réduit la charge de code à écrire, à comprendre et à tenir à jour.

Ce post donne quelques bonnes pratiques pour réduire la redondance des codes.

💡 Supposons qu’on dispose d’une table de données qui utilise le code −99 pour représenter les valeurs manquantes. On désire remplacer l’ensemble des −99 par des NA.

Voici un code Python qui permet de se placer dans ce cas qui, malheureusement, arrive fréquemment.

# On fixe la racine pour être sûr de tous avoir le même dataset
np.random.seed(1234)

# On créé un dataframe
a = np.random.randint(1, 10, size = (5,6))
df = np.insert(
    a,
    np.random.choice(len(a), size=6),
    -99,
)
df = pd.DataFrame(df.reshape((6,6)), columns=[chr(x) for x in range(97, 103)])

Un premier jet de code pourrait prendre la forme suivante:

# Dupliquer les données
df2 = df.copy()
# Remplacer les -99 par des NA
df2.loc[df2['a'] == -99,'a'] = np.nan
df2.loc[df2['b'] == -99,'b'] = np.nan
df2.loc[df2['c'] == -99,'c'] = np.nan
df2.loc[df2['d'] == -99,'d'] = np.nan
df2.loc[df2['e'] == -98,'e'] = np.nan
df2.loc[df2['f'] == -99,'e'] = np.nan

Quelles sont les choses qui vous dérangent dans le code ci-dessus?

Indice 💡 Regardez précisément le code et le DataFrame, notamment les colonnes e et g.

Il y a deux erreurs, difficiles à détecter:

  • df2.loc[df2['e'] == -98,'e'] = np.nan: une erreur de copier-coller sur la valeur de l’erreur ;
  • df2.loc[df2['f'] == -99,'e'] = np.nan: une erreur de copier-coller sur les colonnes en question

On peut noter au moins deux trois :

  • Le code est long et répétitif, ce qui nuit à sa lisibilité;
  • Le code est très dépendant de la structure des données (nom et nombre de colonnes) et doit être adapté dès que celle-ci évolue;
  • On a introduit des erreurs humaines dans le code, difficiles à détecter.

On voit dans la première version de notre code qu’il y a une structure commune à toutes nos lignes de la forme .[. == -99] = np.nan. Cette structure va servir de base à notre fonction, en vue de généraliser le traitement que nous voulons faire.

def fix_missing(x: pd.Series):
    x[x == -99] = np.nan
    return x

df2 = df.copy()
df2['a'] = fix_missing(df['a'])
df2['b'] = fix_missing(df['b'])
df2['c'] = fix_missing(df['c'])
df2['d'] = fix_missing(df['d'])
df2['e'] = fix_missing(df['e'])
df2['f'] = fix_missing(df['f'])

Cette seconde version du code est meilleure que la première version, car on a réglé le problème d’erreur humaine (il n’est plus possible de taper -98 au lieu de -99).

Mais voyez-vous le problème qui persiste ? Le code reste long et répétitif, et n’élimine pas encore toute possibilité d’erreur, car il est toujours possible de se tromper dans le nom des variables.

La prochaine étape consiste à éliminer ce risque d’erreur en combinant deux fonctions (ce qu’on appelle la combinaison de fonctions).

La première fonction fix_missing() sert à régler le problème sur un vecteur. La seconde généralisera ce procédé à toutes les colonnes. Comme Pandas permet une approche vectorielle, il est fréquent de construire des fonctions sur des vecteurs et les appliquer ensuite à plusieurs colonnes.

def fix_missing(x: pd.Series):
    x[x == -99] = np.nan
    return x

df2 = df.copy()
df2 = df2.apply(fix_missing)

Cette troisième version du code a plusieurs avantages sur les deux autres versions:

  1. Elle est plus concise et plus lisible;
  2. Si on a un changement de code pour les valeurs manquantes, il suffit de le mettre à un seul endroit;
  3. Elle fonctionne quels que soient le nombre de colonnes et le nom des colonnes;
  4. On ne peut pas traiter une colonne différemment des autres par erreur.

De plus, le code est facilement généralisable.

Par exemple, à partir de la même structure, écrire le code qui permet de ne traiter que les colonnes a,b et e ne demande pas beaucoup d’énergie.

df2 = df.copy()
df2[['a','b','e']] = df2[['a','b','e']].apply(fix_missing)

(Auto)documentation

Un code sans aucun commentaire est très difficile à s’approprier (y compris pour la personne qui l’a rédigé et qui y revient quelques semaines plus tard). Cependant, un code présentant trop de commentaires est également illisible et reflète généralement un défaut de conception du code qui n’est pas assez explicite.

La documentation vise à présenter la démarche générale, éventuellement à travers des exemples, mais aussi à expliciter certains éléments du code (une opération qui n’est pas évidente, des arguments de fonction, etc.). La documentation se mélange donc aux instructions visant à être exécutées mais s’en distingue. Ces principes sont hérités du paradigme de la “programmation lettrée” (Literate programming) dont l’un des avocats était Donald Knuth.

“Je crois que le temps est venu pour une amélioration significative de la documentation des programmes, et que le meilleur moyen d’y arriver est de considérer les programmes comme des œuvres littéraires. D’où mon titre, « programmation lettrée« .

Nous devons changer notre attitude traditionnelle envers la construction des programmes : au lieu de considérer que notre tâche principale est de dire à un ordinateur ce qu’il doit faire, appliquons-nous plutôt à expliquer à des êtres humains ce que nous voulons que l’ordinateur fasse.

Celui qui pratique la programmation lettrée peut être vu comme un essayiste, qui s’attache principalement à exposer son sujet dans un style visant à l’excellence. Tel un auteur, il choisit , avec soin, le dictionnaire à la main, les noms de ses variables et en explique la signification pour chacune d’elles. Il cherche donc à obtenir un programme compréhensible parce que ses concepts sont présentés dans le meilleur ordre possible. Pour cela, il utilise un mélange de méthodes formelles et informelles qui se complètent”

Donald Knuth, Literate Programming (source)

Cela peut amener à distinguer deux types de documentation:

  1. Une documentation générale de type Jupyter Notebook ou Quarto Markdown qui présente certes du code exécuté mais dont l’objet principal est de présenter une démarche ou des résultats ;
  2. Une documentation de la démarche plus proche du code dont l’un des exemples sont les docstrings Python (ou son équivalent R, la documentation Roxygen).

Les deux grands principes de la documentation au sein d’un script sont les suivants:

  • Il est préférable de documenter le pourquoi plutôt que le comment. Le “comment” devrait être compréhensible à la lecture du code ;
  • Privilégier l’autodocumentation via des nommages pertinents
Comment bien documenter un script ?
  • Minimum 🚦 : commentaire au début du script pour décrire ce qu’il fait
  • Bien 👍 : commenter les parties “délicates” du code
  • Idéal 💪 : documenter ses fonctions avec la syntaxe des docstrings.

Outils et méthodes pour améliorer un code

L’apprentissage par coeur de ces règles ou faire des aller-retour en continu entre le code et les manuels de règles serait quelques peu rébarbatif.

Pour faire le parallèle avec le langage naturel, on n’a pas toujours le bécherelle ou le dictionnaire sous les yeux. Les éditeurs de texte ou les smartphones embarquent des correcteurs orthographiques qui identifient voire corrigent directement le texte écrit.

Il existe le même type d’outils pour les langages de programmation. Python étant l’outil de travail principal de milliers de data-scientists, un certain nombre d’outils ont vu le jour pour réduire le temps nécessaire pour créer un projet ou disposer d’un code fonctionnel. Ces outils permettent un gros gain de productivité, réduisent le temps passé à effectuer des tâches rébarbatives et améliorent la qualité d’un projet en offrant des diagnostics voire des correctifs à des codes perfectibles.

Les principaux outils sont les suivants:

  1. linter : programme qui vérifie que le code est formellement conforme à un certain guidestyle
    • signale problèmes formels, sans corriger
  2. formatter : programme qui reformate un code pour le rendre conforme à un certain guidestyle
    • modifie directement le code
Tip
  • Exemples d’erreurs repérées par un linter :
    • lignes de code trop longues ou mal indentées, parenthèses non équilibrées, noms de fonctions mal construits…
  • Exemples d’erreurs non repérées par un linter :
    • fonctions mal utilisées, arguments mal spécifiés, structure du code incohérente, code insuffisamment documenté…

Linters

Les linters sont des outils qui permettent d’évaluer la qualité du code et son risque de provoquer une erreur (explicite ou silencieuse).

Voici quelques exemples de problèmes que peuvent rencontrer les linters:

  • les variables sont utilisées mais n’existent pas (erreur)
  • les variables inutilisées (inutiles)
  • la mauvaise organisation du code (risque d’erreur)
  • le non respect des bonnes pratiques d’écriture de code
  • les erreurs de syntaxe (par exemple les coquilles)

La plupart des logiciels de développement embarquent des fonctionalités de diagnostic (voire de suggestion de correctif). Il faut parfois les paramétrer dans les options (ils sont désactivés pour ne pas effrayer l’utilisateur avec des croix rouges partout).

En Python, les deux principaux linters sont PyLint et Flake8. Dans les exercices, nous proposons d’utiliser PyLint qui est pratique.

Tip

L’un des intérêts d’utiliser PyLint est qu’on obtient une note, ce qui est assez instructif. Nous l’utiliserons dans l’application fil rouge pour comprendre la manière dont chaque étape améliore la qualité du code.

Il est possible de mettre en oeuvre des pre commit hook qui empêchent un commit n’ayant pas une note minimale.

Formaters

Le formater modifie directement le code. On peut faire un parallèle avec le correcteur orthographique.

Cet outil peut donc induire un changement substantiel du script afin de le rendre plus lisible.

Le formater le plus utilisé est Black.

Note

Pour signaler sur Github la qualité d’un projet utilisant Black, il est possible d’ajouter un badge dans le README:

Code style: black

Isoler la configuration du code

L’exécution d’un code peut dépendre d’éléments de configuration comme des jetons d’authentification ou des mots de passe de connexion à des services qui sont personnels. Ces éléments de configuration n’ont pas vocation à être partagés par du code et il est recommandé de les exclure du code. La meilleure manière de transformer ces configurations en paramètre est de les isoler dans un script séparé, qui n’est pas partagé, et utiliser les variables créées à cette occasion dans le reste du programme.

La manière privilégiée de conserver ce type d’information est le format YAML. Ce format de fichier permet de stocker des informations de manière hiérarchisée et flexible mais de manière plus lisible que le JSON. Ce format sera transformé en dictionnaire Python ce qui permet des recherches facilitées.

Prenons le YAML suivant:

# secrets.yaml
token:
    api_insee: "toto"
    api_github: "tokengh"
pwd:
    base_pg: "monmotdepasse"

L’import de ce fichier se fait avec le package yaml de la manière suivante:

import yaml

with open('secrets.yaml') as f:
    secrets = yaml.safe_load(f)

jeton_insee = secrets['token']['api_insee']
# utilisation du secret

Méthodes complémentaires pour favoriser la qualité d’un script

L’opensource comme moyen pour améliorer la qualité

En ouvrant son code, il est possible de recevoir des suggestions voire des contributions de réutilisateurs du code. Cependant, les vertus de l’ouverture vont au-delà.
En effet, l’ouverture se traduit généralement par des codes de meilleur qualité, mieux documentés pour pouvoir être réutilisés ou ayant simplement bénéficié d’une attention accrue sur la qualité pour ne pas paraître ridicule. Même en l’absence de retour de (ré)utilisateurs du code, le partage de code améliore la qualité des projets.

Revue de code

La revue de code s’inspire de la méthode du peer reviewing du monde académique pour améliorer la qualité du code Python. Dans une revue de code, le code écrit par une personne est relu et évalué par un ou plusieurs autres développeurs afin d’identifier les erreurs et les améliorations possibles. Cette pratique permet de détecter les erreurs avant qu’elles ne deviennent des problèmes majeurs, d’assurer une cohérence dans le code, de garantir le respect des bonnes pratiques mais aussi d’améliorer la qualité du code en identifiant les parties du code qui peuvent être simplifiées, optimisées ou refactorisées pour en améliorer la lisibilité et la maintenabilité.

Un autre avantage de cette approche est qu’elle permet le partage de connaissances entre des personnes expérimentées et des personnes plus débutantes ce qui permet à ces dernières de monter en compétence. Github et Gitlab proposent des fonctionnalités très pratiques pour la revue de code: discussions, suggestions de modifications…

Références

Footnotes

  1. Guido Van Rossum est le créateur de Python, c’est donc quelqu’un qu’il est pertinent d’écouter.↩︎

  2. Par exemple, en R, il est possible d’utiliser <- ou = pour l’assignation, on ne recontre pas d’erreur en cas de mauvaise indentation…↩︎

  3. Il existe d’autres guides de style notamment le MLR style guide qui est un framework orienté objet de Machine Learning en R.↩︎

  4. Nous allons d’ailleurs plutôt recommander l’autodocumentation que la multiplication de commentaires dans un document qui ne seront jamais lus ou qui ne seront pas actualisés en même temps que le code qu’ils accompagnent.↩︎

  5. Par exemple, à la suite d’une série de conditions if (selon les cas une liste, un vecteur, un DataFrame, etc.)↩︎

  6. Nous présenterons les deux approches principales en Python, leurs points commun et les points par lesquels ils diffèrent : les environnements virtuels (gérés par un fichier requirements.txt) et les environnements conda (gérés par un fichier environment.yml)↩︎