Coder avec les type hints : une pratique simple qui optimise votre business
Python Bonnes Pratiques

Coder avec les type hints : une pratique simple qui optimise votre business

Valentin Chab | | 8 min de lecture

Dans le monde du développement logiciel, les petits détails font de grandes différences. Adopter des bonnes pratiques comme l’utilisation des type hints en Python peut sembler un changement mineur, mais ça a un impact direct sur la qualité du code, l’efficacité de l’équipe et la scalabilité du business.

Moins d’erreurs, plus de lisibilité, une meilleure collaboration entre développeurs : tout ça se traduit en moins de temps perdu, moins de dette technique et plus de valeur livrée.

Dans cet article, on va vous présenter un outil très utile pour corriger notre code, éviter les erreurs et garantir que quiconque viendra lire notre code après nous ne s’arrachera pas les cheveux. C’est parti !

C’est-à-dire, type, rien

Python est un langage à typage dynamique, ce qui signifie que les variables peuvent changer de type de données au cours de l’exécution d’un script, contrairement aux langages à typage statique où l’on déclare le type de chaque variable avant la compilation. Ça le rend plus flexible et offre plus de possibilités, mais ça le rend aussi plus sensible à certaines erreurs.

De quoi parle-t-on ? Voici une fonction appelée multi_call définie dans un fichier appelé multi.py.

Fonction multi_call avec valeur par defaut

C’est une fonction tout à fait normale : elle prend deux arguments, num et alt, et alt a une valeur par défaut égale à False. Si alt est égal à True, la sortie de notre fonction sera num multiplié par 5. Si on laisse la valeur par défaut de alt, l’exécution renverra num multiplié par -2.

Quand on définit un argument de fonction, si on n’explicite pas le type de données attendu, l’interpréteur Python identifiera qu’il s’agit d’une variable de type “Any” ; c’est-à-dire qu’il accepte n’importe quel type de données en entrée.

Fonction multi_call sans typage

Dans le cas de cette fonction, en définissant nous-mêmes une valeur par défaut booléenne pour l’argument alt, Python s’attendra à un type de données identique, donc il accepterait True ou False mais pas 1, 3.7 ou “true”. Mais que se passe-t-il si on ne veut pas indiquer de valeurs par défaut ? Comment peut-on aider à rendre notre code plus lisible et moins sujet aux erreurs et bugs ?

Il existe des outils et bonnes pratiques qui nous aident à définir et expliciter le type de données que l’on assigne à chaque variable comme une couche de contrôle supplémentaire. L’un d’eux est le type hint.

Un indice pour vous…

Quand on indique que la valeur par défaut de alt est False, l’interpréteur Python détermine que c’est un argument qui prend des valeurs booléennes. alt=False est équivalent à écrire alt: bool = False et, dans cette seconde syntaxe, on dit “l’argument alt prend des valeurs booléennes et a pour défaut la valeur False”. Dans les versions modernes de Python (3.10 et plus), on peut aussi utiliser une syntaxe plus concise avec l’opérateur | pour définir des types optionnels : par exemple, alt: bool | None si on acceptait aussi des valeurs nulles.

C’est utile pour éliminer l’ambiguïté et prévenir les erreurs à l’exécution, et c’est quelque chose qu’on peut faire explicitement avec tous les paramètres d’une fonction donnée. À partir de Python 3.9, on peut même utiliser une syntaxe plus simple et native comme list[int] ou dict[str, float], sans avoir besoin d’importer List ou Dict depuis typing.

Observons la fonction multi_call() mise à jour avec plus d’informations :

Fonction multi_call typee

Ici, on voit clairement que le développeur qui a écrit cette fonction a indiqué que l’argument num doit prendre des valeurs entières, une définition logique puisque ça sera ensuite multiplié par 5 ou -2 selon la condition booléenne imposée par l’argument alt. Mais en plus, on ajoute en dehors des parenthèses des arguments de la fonction un -> int, qui précise le type de données de la sortie de notre fonction.

Ces “petits coups de pouce” qu’on donne à notre fonction s’appellent des “type hints”, des indices de types, et c’est un excellent outil pour garder notre code propre, lisible et sans bugs. Ils ont été présentes dans le PEP 484 (Python Enhancement Proposal, un document d’information pour la communauté Python qui propose des améliorations ou décrit de nouvelles fonctionnalités ou processus) et sont devenus une pratique courante dans tous types de développements en Python.

L’un des avantages est de pouvoir survoler une variable avant son affectation pour que l’interpréteur nous indique à l’avance quel sera le type de la variable résultante. Ça semble évident et trivial dans une fonction simple comme celle qu’on analyse, mais dans des fonctions plus complexes ça peut nous faire gagner pas mal de temps d’analyse de code et de types de données.

Maintenant, si on appelle multicall et qu’on passe 7.2 pour l’argument _num, c’est-à-dire un float au lieu d’un int, la fonction s’exécutera quand même. On entend déjà votre frustration de l’autre côté de l’écran. Pourquoi, si on a spécifié le type avec un type hint ? Parce que les hints ne forcent pas le type de données que la fonction prend, ils facilitent son utilisation pour notre utilisateur. Si on voulait bloquer une exécution qui ne correspond pas aux hints qu’on a donnés, il faudrait utiliser une syntaxe similaire à celle-ci qui étend notre code :

Fonction multi_call avec assert

Dans cette fonction modifiée, c’est la déclaration assert qui valide le type de données.

Donc, on sait maintenant que les type hints sont une bonne pratique pour la lisibilité et le débogage de notre code. Il est temps de vous présenter un outil qui va nous faciliter la vie encore davantage, ainsi que celle de toute personne qui devra travailler avec ces scripts.

Qui veut la fin veut les moyens

Avant d’aller plus loin, j’aimerais faire une brève parenthèse pour vous parler des linters, un composant très utile et dans de nombreux cas crucial pour garantir la lisibilité, la qualité et le respect des standards et bonnes pratiques de notre code.

Un linter (le terme vient d’un utilitaire Unix utilisé pour trouver des erreurs dans du code écrit en C) est un outil qu’on peut intégrer à notre IDE pour aider au développement de code via la détection d’erreurs potentielles, bugs, inconsistances de style et autres problèmes. Il existe des linters de différents types pour trouver des erreurs très variées ; à chaque développeur de trouver celui qui lui convient le mieux.

Voyons donc une bibliothèque spécialement conçue pour automatiser la vérification des type hints : MyPy. L’idée de ce linter est qu’il incorpore des éléments d’un langage à typage statique dans Python pour tester les fonctions que l’on définit dans notre code en vérifiant que les types de données déclarés comme type hints correspondent aux variables appelées par la suite. Ça nous permet, sans exécuter les scripts, de garantir un typage cohérent des variables et des sorties de nos fonctions.

L’installation se fait avec la commande !pip install, comme la plupart des bibliothèques standard.

Selectionner MyPy dans VSCode

Il est important de noter que MyPy ne fonctionne pas pour les Jupyter Notebooks (l’une des options préférées des data scientists pour prototyper des scripts), mais bien pour analyser des fichiers entiers.

Ci-dessous, on voit l’exemple de classes.py :

  • On a un init qui prend trois arguments : a, b et c, avec les types int, str et float respectivement.
  • On a paramétré a à 1, b à 1 et c à “asdad” (aussi connu sous le nom de “tête sur le clavier”).
  • Le type de données qu’on a indiqué pour notre paramètre a est correct ; 1 est un int. Mais b et c ont été déclarés avec des types de données incorrects par rapport à ce qu’on a spécifié comme type hint. Si on regarde de près, quand MyPy est sélectionné comme linter, dans notre affectation de A, les paramètres b et c sont soulignés d’un petit trait rouge indiquant une erreur.

DataClass avec type hints et erreur

Rappelons une fois de plus que passer des paramètres à nos fonctions avec des types de données différents de ceux indiqués en type hints ne signifie pas que l’exécution sera erronée. Quand on corrige les types de données passés en paramètres pour qu’ils correspondent à ce qu’on a indiqué, les lignes rouges sous les arguments disparaissent.

Maintenant, voyons comment on peut analyser le fichier contenant notre classe avec MyPy.

MyPy erreurs de typage

Si on appelle le fichier multi.py qui contient multi_call (qui, soit dit en passant, se trouve dans le même répertoire que ce notebook qu’on exécute) avec la commande !mypy, on voit que dans la sortie de cette cellule la bibliothèque a immédiatement détecte les inconsistances dont on parlait.

Une autre option est d’appeler directement !mypy, ce qui analysera tous les fichiers .py du répertoire.

MyPy erreurs multiples fichiers

Mais j’en veux encore…

C’est tout pour ce premier article. On a vu comment l’utilisation des type hints et d’outils comme MyPy n’améliore pas seulement la qualité du code, mais impacte aussi l’efficacité de l’équipe, réduit les erreurs évitables et facilite la maintenance des projets en croissance. Dans des contextes où le logiciel fait partie du coeur de métier, écrire du code plus clair, lisible et contrôle n’est pas qu’une question technique, c’est une décision stratégique.

Et si en plus de suggérer des types, on pouvait les valider automatiquement à l’exécution ? Il existe une bibliothèque conçue exactement pour ça… mais on vous en parle dans le prochain volet.


Références supplémentaires :

  1. PEP 484 – Type Hints
  2. Documentation officielle de MyPy
  3. Linting dans VSCode
  4. Python Typing Module
Partager

Vous avez un vrai problème technique ?

On ne vend pas de solutions génériques. Parlons de ce que vous devez résoudre.

Parlons-en