Typage en python : comment utiliser le type hinting pour debugger du code
Python est sans doute le langage de programmation le plus en vogue depuis quelques années. Étant donné son excellente conception algorithmique, il est très répandu dans l’univers de l’analyse de données et du machine-learning. En comparaison avec d’autres langages, Python s’appuie sur une syntaxe intuitive qui encapsule des concepts d’implémentation de bas niveau ce qui rend le code agréable à lire et facilement compréhensible par l’œil humain.
Cette simplicité a généré un fort intérêt des développeurs. Ainsi, il connait un essor important dans le développement d’applications web. L’un des points positifs du langage est le gain en productivité pendant le développement d’applications web par rapport à d’autres langages à typage statique comme Java ou C++. Par conséquent, un gain de temps précieux pour les entreprises qui font de plus en plus le choix de Python.
Paradoxalement, la simplicité de Python peut devenir problématique. Les applications sont plus rapidement en production mais elles peuvent également contenir plus de bugs. L’une des critiques souvent évoquée sur Python est son typage dynamique. En effet, le type de variables est assigné lors de la déclaration et il peut être modifié pendant l’interprétation du code.
Pour palier cette problématique, Python 3.5 a introduit le « type hinting » (PEP484: Type Hints). Dans cet article on expliquera comment fonctionne le typage en python, quelles stratégies adopter afin d’exploiter le type hinting dans une base de code et comment vérifier les types pendant l’exécution d’un programme. Les examples dans cet article sont compatibles avec python 3.8.
Comment typer en python ?
Le typage se fait grâce aux annotations. Elles permettent d’associer un type donné (List
, bool
, etc) aux arguments et aux retours des fonctions.
MyPy, static type checker : Cet outil met en évidence les incohérences dans le code en vérifiant les annotations de type.
Ces annotations sont des indices pour le static type checker
de MyPy. Ainsi, le type hinting ne modifie pas le fonctionnement
pendant l’exécution Python. En revanche, Il donne de la visibilité sur les potentiels bugs pendant le développement.
from typing import List, Dict, Tuple, Dict, Iterable, Sequence, Mapping, Union, Any
Les types primitifs sont les plus récurrents et utilisés : bool
, int
, str
, float
. Il peuvent être utilisés pour typer
les arguments ainsi que les retours des fonctions. Dans l’exemple suivant, la fonction circle_surface
prend en argument
le rayon du cercle (le radius
) et calcule la surface de ce cercle. Cet argument est de type float
(indiqué après
le :
suivant le nom de l’argument) et la réponse est elle aussi de type float
(le type de retour est indiqué après
la flèche ->
).
def circle_surface(radius: float) -> float:
return 3.141516 * math.sqrt(radius)
Il est également possible de créer des types composés comme des listes d’entiers (integer
) ou comme dans cet example des
listes de nombres flottants (floats
).
from typing import List
Vector = List[float]
def scale(scalar: float, vector: Vector) -> Vector:
return [scalar * num for num in vector]
Le typage peut se faire également sur toutes les classes définies dans un programme y compris celles des librairies externes.
# l'utilisation des type alias rend la lecture
# plus claire et la maintenance du code plus simple.
MongoPipeline = List[Mapping[str, Any]]
MongoQuery = Union[Mapping[str, Any], MongoPipeline]
def get_entity(mongo_query: MongoQuery) -> Optional[Entity]:
if result := MongoClient().collection.find(mongo_query)
return Entity(**result)
Bien que ce soit possible, il est parfois risqué de typer des arguments qui proviennent de librairies externes si elles n’utilisent pas le typage. Le contrat qu’un argument doit respecter est implicite. Ce sont donc les mainteneurs des librairies externes qui doivent définir les types de manière explicite. D’autre part, les types des librairies peuvent évoluer avec le temps.
# mongo < 4.4
MongoQuery = Mapping[str, Any]
# mongo == 4.4
MongoPipeline = List[Mapping[str, Any]]
MongoQuery = Union[Mapping[str, Any], MongoPipeline]
Ainsi l’utilisation de types alias
dans l’exemple ci-dessus n’est pas forcement une bonne idée. Il est cependant
important d’évoquer l’existence des alias qui peuvent être utilisés dans son propre code.
L’utilisation des ABC
(Abstract Base Classes) est à privilégier aux types concrets. Ainsi, Iterable
permet
de typer une fonction qui prend différents types en argument tels que tuple
, list
ou set
. Plus restrictif, le type
concret list
n’accepte que des listes.
Les retours implicites ainsi que les fonctions sans retour sont également pris en compte par MyPy.
def print_variable(s: str) -> None:
print(s)
def not_ready(s: str) -> NoReturn:
print('not implemented')
raise
Mais comment utiliser concrètement le type hinting pour debugger du code ?
Deux stratégies simples :
-
Appliquer les type hinting sur les parties du code (classes, fonctions, variables d’itérations, etc) où il y a des erreurs de type. MyPy donnera de la visibilité sur les incohérences de types et cela vous permettra de mieux comprendre d’où vient le problème.
-
Utiliser les fonctions
reveal_type(exp)
et/oureveal_locals
. Elles produisent un rapport sur les expressions en paramètre ou sur les variables dans le scope.
Python propose davantage de types tels que les typing.Optional
, typing.TypedDict
et @dataclass
.
Typeguard: vérification du type à l’exécution du programme
Avec la librairie Typeguard on peut exécuter une application dans un serveur de test et/ou local afin de chercher les incohérences de typing dans le code via un IHM ou un script. Ainsi, il est possible de faire de tests avec des données réelles.
Il existent trois manières différentes de tester les types avec Typeguard :
check_argument_types()
etcheck_return_type()
: ces fonctions doivent être ajoutées dans les fonctions avec unassert
.- le décorateur
@typechecked
install_import_hook()
: ajoute le décorateur@typechecked
à toute fonction typée.
The Zen of Python
La PEP20 (The Zen of Python) décrit en quelques postulats la philosophie du langage. Python est conçu sur des principes qui prônent, entre autres, la beauté, la simplicité, la lisibilité et la flexibilité. Personnellement, je trouve que ces aspects ont été respectés au cours des différentes versions. Le passage de Python 2 à Python 3 d’une application comme Deepki Ready a été plus simple et transparent que certaines mises à jour de librairies.
L’introduction du type hinting dans l’univers Python a été, à mon avis, une bonne manière de palier les problématiques des bugs insidieux qui passent entre les mailles du filet malgré la présence de tests unitaires. De la même manière, le choix laissé au dévelopeur sur la stratégie d’implémentation et le dégré de restrictivité se conjugue avec la philosophie Python.
Le type hinting est un sujet toujours présent dans les dernières versions de Python. La nouveauté en Python 3.9 est l’utilisation
des built-in collections
comme des types génériques. Ainsi, on n’a plus besoin d’importer les types en majuscules de typing
(from typing import List, Dict
). N’hésitez pas à consulter la PEP585: Type Hinting Generics In Standard Collections
def greet_all(names: list[str]) -> None:
for name in names:
print("Hello", name)
On peut donc conclure que le sujet sera au cœur de l’évolution du langage. Ainsi la critique, souvent évoquée par la communauté de développeurs, concernant le typage et les problèmes sous-jacents ne devrait plus être d’actualité prochainement.
Cette entrée a été publiée dans programmation avec comme mot(s)-clef(s) python, type hinting
Les articles suivant pourraient également vous intéresser :
- La bonne et la mauvaise review par Sébastien Bizet
- Dark mode vs Light mode : accessibilité et éco-conception par Jean-Baptiste Bergy
- Principes SOLID et comment les appliquer en Python par Mariana ROLDAN VELEZ
- Pydantic, la révolution de python ? par Pablo Abril
- Comment utiliser les fixtures pytest en autouse tout en maîtrisant ses effets de bord ? par Amaury Boin
Postez votre commentaire :
Votre commentaire a bien été envoyé ! Il sera affiché une fois que nous l'aurons validé.
Vous devez activer le javascript ou avoir un navigateur récent pour pouvoir commenter sur ce site.