Retours d'expérience sur Mypy ?

Le typage statique en Python, est-ce pertinent ?

a marqué ce sujet comme résolu.
Auteur du sujet

Bonjour à tous,

J’aurais aimé savoir si certains d’entre vous ont déjà pu utiliser Mypy dans un projet (perso, ou dans votre boîte, en solo, ou au sein de votre équipe) et qu’est-ce que du typage statique sur le Python a pu vous apporter de bien ou de mal. Avez-vous déjà eu l’occasion de porter un code Python en Python typé ? Ou bien de commencer un projet en Python typé from scratch ? Ça m’intéresse de savoir ce que vous en avez pensé !

Je suis actuellement en train de lancer un projet dans lequel je serai certainement amené à collaborer avec d’autres personnes. L’application qui y est développée n’a rien de très exotique : une app Web faite avec du Flask et une base de données PostgreSQL. Ça reste une application CRUD assez basique à l’heure actuelle, mais j’attends une certaine montée en complexité du code dans les étapes à venir. J’ai du mal à voir où le typage sur Python (total ou partiel) pourrait m’aider dans mon projet : après tout, il y a déjà une certaine rigueur sur la validation des inputs (avec l’excellente lib Cerberus par exemple), du try/except autour des erreurs SQL de violation de contraintes, etc. D’un autre côté, si cela peut améliorer la qualité du code de façon tangible, je ne voudrais pas passer à côté de ça.
D’où mon intérêt pour vos précieux retours.

Pour l’avoir essayé déjà un peu, je dois dire que je ne suis pas si déstabilisé par rapport à la verbosité qui augmente un peu. Ça ajoute des infos utiles, je trouve. J’ai pas mal apprécié que Mypy soit capable de détecter des petites erreurs d’inattention du genre, le type retourné par d.get('foo'), n’est pas celui de d['foo'] (s’il est là) mais bien Optional[type(d['foo'])], ce qui force à considérer le cas où l’on aura None ou bien de renseigner une valeur par défaut.

Merci de m’avoir lu !

Édité par sgble

+0 -0

J’utilise peu, voire pas mypy, par contre j’aime bien les annotations pour trois raisons:

  • elles me permettent de montrer mon intention: si tu as un Optional c’est clair que c’est parce que je veux que None soit retourné dans certain cas
  • elles permettent à mon IDE de faire de l’autocomplétion et de l’enrichissement contextuel bien plus facilement (affichage de la doc, affichage de warning quand je passe un truc qui respecte pas le type)
  • elles permettent de prototyper de manière iterrative tout en gardant les idées claires : si je veux une fonction qui prend un string et retourne un boolean c’est écrit clairement, en plus ça permet de dire clairement ce qu’on attend : le cas typique c’est quand on a des path : attends-je un str ou un pathlib.Path, avoir l’annotation permet de savoir que la fonction est capable de s’occuper d’un seul uniquement ou des deux.
+1 -0

Salut ! Je suis justement en train d’ajouter le typage statique sur mon projet. Pour le contexte, c’est un projet de stage commencé il y a 2 mois en équipe. J’aime bien le principe car :

  • Ça simplifie la revue de code (on sait quel est le type de chaque argument d’une fonction et je trouve ça plus clair que de la documentation)
  • Permet de mieux penser l’architecture en amont (cette fonctionva prendre une str mais est-ce que ça vaut le coup qu’elle gère aussi une re ?
  • Ça m’oblige à savoir exactement ce qui est supporté. Quand je me base sur une lib déjà existante, je sais si je restreint les possibilité ou non.
+1 -0
Auteur du sujet

Hello,

Désolé de la latence de ma réponse, et merci pour les vôtres :)

  • elles permettent à mon IDE de faire de l’autocomplétion et de l’enrichissement contextuel bien plus facilement (affichage de la doc, affichage de warning quand je passe un truc qui respecte pas le type)

Je n’ai pas pensé à regarder s’il y a un plugin Mypy intéressant pour Vim. S’il y en a un, ça peut carrément me motiver à l’utiliser d’autant plus !

  • elles permettent de prototyper de manière iterrative tout en gardant les idées claires : si je veux une fonction qui prend un string et retourne un boolean c’est écrit clairement, en plus ça permet de dire clairement ce qu’on attend : le cas typique c’est quand on a des path : attends-je un str ou un pathlib.Path, avoir l’annotation permet de savoir que la fonction est capable de s’occuper d’un seul uniquement ou des deux.
artragis

Tout à fait, c’est quelque chose que je trouve assez appréciable. Parfois, je ne sais pas trop quoi envoyer dans une fonction même si son nom est explicite et clair. Exemple concret similaire : à mon ancien job, il y avait cette fonction qui prenait en argument un file descriptor en entrée pour y écrire des choses. Mais je savais pas quoi mettre… un int étant la référence du FD ? Un objet de type file object ? La signature aurait pu me signaler dès le début que la fonction prenait bien un int en entrée, et pas un file object. En Python, on a souvent ce genre de cas, du coup je me surprends parfois à faire du ad-hoc selon ce que j’ai en entrée. Typiquement : si j’ai un int en entrée, alors tout roule, sinon, je fais un param.fileno(). Je n’aime pas beaucoup cette habitude, le mieux serait de définir clairement dès le début des fonctions précises, à mon avis.

Ça simplifie la revue de code (on sait quel est le type de chaque argument d’une fonction et je trouve ça plus clair que de la documentation)

Intéressant. As-tu eu l’occasion de travailler sur les revues de code quand tu n’avais pas encore commencé à ajouter le typage ?

De façon générale, j’ai un peu peur que le typage sur un langage conçu dynamique le rende un peu schizophrène à terme… Pour JS, un des parti pris a été de repenser totalement un langage à part : le TypeScript. Mais il n’y a pas eu de langage statique "Python-like", à ma connaissance. Je suis bien curieux de voir ce que Mypy va donner, à terme…

+0 -0

Si vous ne comprenez pas les principes et fonctionnement du typage dynamique, ou si vous ne les appréciez pas, à défaut de les apprendre, pourquoi ne pas simplement utiliser un langage à typage statique, au-lieu de les bafouer avec des outils et des concepts qui vont à l’encontre de leur nature et de propager des mauvaises pratiques ?

+0 -2

Oui. C’est mon opinion. Et quand je lis les raisons de son utilisation citées ici, j’en suis d’autant plus convaincu.

Les annotations elles-mêmes incitent les utilisateurs du langage à ne pas penser correctement en Python.
Pourquoi utiliser mypy alors qu’un langage à typage statique serait plus efficace avec cette manière de penser ?

+0 -1

Les annotations elles-mêmes incitent les utilisateurs du langage à ne pas penser correctement en Python.

lol.

Pourquoi utiliser mypy alors qu’un langage à typage statique serait plus efficace avec cette manière de penser ?

parce que tu as tort.

mypy (et les annotation du module typing) ne sont pas un système de typage mais d’aide à l’augmentation de la qualité du code et de son débugage.

Ce statu "d’aide" fait que les annotations sont totalement dispensable et que c’est au développeur de décider de leur pertinence dans son programme.

Programmer c’est avant tout lire du code et avoir une aide qui te rend la lecture plus claire notamment sur les intentions du développeur c’est totalement indépendant de l’aspect dynamique ou non du typage.

+0 -1

Les annotations et mypy sont totalement compatibles avec les valeurs de Python, notamment le duck-typing / typage structurel, il n’y a qu’à regarder les types définis dans le module typing.

Les seuls cas qui sortiraient du cadre de mypy seraient des objets auxquels des méthodes seraient ajoutées à la volée, mais c’est un cas d’utilisation suffisamment rare pour se permettre d’utiliser pour ces objets là des annotations plus permissives (typing.Any).

Auteur du sujet

Si vous ne comprenez pas les principes et fonctionnement du typage dynamique, ou si vous ne les appréciez pas, à défaut de les apprendre, pourquoi ne pas simplement utiliser un langage à typage statique, au-lieu de les bafouer avec des outils et des concepts qui vont à l’encontre de leur nature et de propager des mauvaises pratiques ?

psycopy

En ce qui me concerne, je n’ai rien contre le typage dynamique.
Je dirais qu’avoir la possibilité de travailler en statique sans changer, ni ajouter de langage est un avantage certain (en partant du principe que Mypy tienne ses promesses, chose sur laquelle je n’ai encore vraiment d’avis), par exemple : j’ai un gros programme en Python et il y a un module assez critique sur lequel j’aimerais obtenir plus de garantie. J’écris ce module en Python typé, checké avec Mypy et ça me permet de maintenir ma stack telle quelle.
On pourrait certes utiliser un langage comme OCaml de façon exclusive, mais qu’en est-il des projets déjà existants qui sont écrit en Python ? Faut tout ré-écrire ?

Je pense que je comprends ton propos, sur un plan fondamental. Mais je crois que le problème résolu par Mypy est tout sauf fondamental. Il s’agit ici de pragmatisme pour l’industrie, il me semble.

Guido van Rossum, second plus gros contributeur à mypy, bafouerait le langage Python et propagerait de mauvaises pratiques ?

Je me dis que si GvR est là-dedans, c’est qu’il n’oserait pas « dénaturer » Python. Et ça pourrait être dangereux si c’était le cas : on aurait potentiellement un cas de scission entre les pythoneux dynamiques et les pythoneux statiques. Typiquement, les uns ne contribueraient plus aux projets Python des autres (parce qu’il ne connaissent pas Mypy, par exemple). Ça ferait de facto deux langages distincts…

+0 -0

Peut-être que ce n’est pas votre cas ici, pour vous qui avez suffisamment d’expérience et de recule pour utiliser ces outils à leur juste fonction - encore que ça semble plus être lié à une question de lisibilité que je ne partage pas - mais ce n’est pas le cas de la majorité.

Ces outils influencent leurs utilisateurs à ne pas comprendre le duck-typing, à s’en défaire, à penser tout leur développements comme s’ils utilisaient un langage à typage statique.

Bref, c’était mon opinion, je pense qu’il est plus judicieux de changer de langage que de changer les principes d’usage d’un langage.

+0 -0

Ces outils influencent leurs utilisateurs à ne pas comprendre le duck-typing, à s’en défaire, à penser tout leur développements comme s’ils utilisaient un langage à typage statique.

donne des preuves. là tu donnes juste d’un côté des assertions ambitieuses ("Les annotations elles-mêmes incitent les utilisateurs du langage à ne pas penser correctement en Python") puis des généralités floues et fondées sur des impressions.

Désolé, mais je ne vois pas en quoi les annotation (un truc qui rend les choses ultra dynamique en soi) empêcherait de penser de manière pythonique.

En soi on ne change pas "les principes d’usage" avec python et mypy. On a juste des vérifications supplémentaires pour assurer la cohérence et éviter des "attributeerror" totalement non maîtrisé.

+0 -0

Je pense qu’il veut dire que certaines personnes auront tendance à restreindre par les annotations les paramètres de fonction à des types tels que str là où tout objet avec une méthode __str__ pourrait être accepté.

Mais je doute que ce soit une peur fondée, on voit par exemple que typing.Iterable est souvent utilisé.

Pour beaucoup ces annotations sont un moyen de restriction de type. Et même si la restriction en réalité est faible ou inexistante, elle a un impact sur la manière de penser le code. Ils ne pensent plus duck-typing ou interface, mais uniquement typage.
Ils oublient que ce n’est pas le type qui importe, mais ses méthodes.

Ça alourdi le code, le rend bien moins digeste. Pour du debug, les doctests sont plus pratiques et plus propres (à mon sens tout du moins).

+0 -0
Auteur du sujet

Ces outils influencent leurs utilisateurs à ne pas comprendre le duck-typing, à s’en défaire, à penser tout leur développements comme s’ils utilisaient un langage à typage statique.

psycopy

Je viens de lire un paragraphe intéressant de la doc de Mypy. Donc si j’ai bien suivi, Mypy supporte à la fois le typage dit structurel et le nominal. Si j’ai bien saisi la nuance entre les deux, je suppose alors que tu parles ici de typage nominal quand tu parles du langage à typage statique dont pourrait faire usage les gens qui ne comprennent pas le duck-typing.

Mais du coup, puisque Mypy supporte aussi le typage structurel, cela ne semble pas aller contre le duck-typing. Voici ce que l’on peut lire :

Structural subtyping can also be useful. Class D is a structural subtype of class C if the former has all attributes and methods of the latter, and with compatible types.

Structural subtyping can be seen as a static equivalent of duck typing, which is well known to Python programmers. Mypy provides support for structural subtyping via protocol classes described below. See PEP 544 for the detailed specification of protocols and structural subtyping in Python.

Protocols and structural subtyping

(la mise en gras est de ma part)

+1 -0

Pour beaucoup ces annotations sont un moyen de restriction de type. Et même si la restriction en réalité est faible ou inexistante, elle a un impact sur la manière de penser le code. Ils ne pensent plus duck-typing ou interface, mais uniquement typage.
Ils oublient que ce n’est pas le type qui importe, mais ses méthodes.

psycopy

En fait non, pour beaucoup les annotations (et le typage statique apporté par mypy) viennent simplement renforcer les restrictions déjà existantes. Sauf qu’au lieu de simplement avoir une TypeError, ValueError ou AttributeError lors de l’exécution, l’erreur est ici décelée par une analyse statique.

Perso, au boulot, je n’attends que de me débarrasser de python 3.5 pour utiliser mypy.

Les craintes sur le fait de typer le code (et donc l’alourdir ou le figer, etc.) sont assez communes, mais je pars d’un constat simple : dans notre codebase, on a d’autres problèmes plus immédiats que le fait que le code soit correctement duck-typé ou non.

Typiquement :

  • Le type attendu/retourné/yieldé est documenté dans les docstrings, mais il leur arrive de mentir.
  • Parfois, je tombe sur des fonctions qui retournent None au lieu de lever une exception, sans que ce ne soit documenté. C’est un truc contre lequel je me bats, d’autant que je le découvre souvent un peu trop tard.
  • Il arrive que l’on veuille changer une fonction (le type de ses paramètres) pour la faire évoluer : quand c’est le cas, on pense bien sûr à modifier les tests de ces fonctions mais on doit généralement serrer les dents (pour ne pas dire les fesses) en espérant qu’on n’a pas oublié un endroit où cette fonction est appelée en utilisant l’ancienne signature.

Ces trois cas là sont détectés facilement par Mypy, et il suffit, pendant la review, de s’assurer que les API publiques sont correctement annotées, ce qui fait gagner du temps à tout le monde.

Bref TL;DR : on peut légitimement craindre de rendre un code moins pythonique en annotant ses types, mais honnêtement, dans une codebase Python de la vraie vie (je parle de 60k lignes de code utiles en Python, là), qui évolue parfois dans l’urgence, si c’était la plus grosse crainte qu’on devait avoir et la chose la plus importante à laquelle on devrait faire attention, ce serait le rêve.

Édité par nohar

I was a llama before it was cool

+0 -0

@nohar : n’as-tu pas l’impression d’atteindre les limites du language ? Je comprends bien qu’il n’est pas envisageable de recommencer de zéro, mais regrettes-tu le choix de Python sur un projet aussi gros ? Si oui, tu pencherais vers un language avec quel sorte de typage ? Statique fort ?

@nohar : n’as-tu pas l’impression d’atteindre les limites du language ? Je comprends bien qu’il n’est pas envisageable de recommencer de zéro, mais regrettes-tu le choix de Python sur un projet aussi gros ? Si oui, tu pencherais vers un language avec quel sorte de typage ? Statique fort ?

tleb

Non, je pense que Python est vraiment tout indiqué pour ce projet et cette équipe. C’est juste que la codebase a un passif un peu one again dans lequel on se permettait toutes sortes de hacks pas terribles, et qu’au rythme où le code évolue on a besoin d’outils pour maintenir une bonne hygiène.

Si c’était à refaire, ce n’est pas le langage que je changerais mais le design et les exigences sur la qualité du code qui seraient un peu différentes (elles ont évolué dans le temps), et renforcées dès le départ.

Dans toute autre langage, on n’aurait jamais pu avoir la réactivité qu’on a avec Python. Par contre clairement je pense que les jour où on aura du temps, certaines briques bas niveau vont bouger pour être implémentées en Rust et bindées en Python, mais ça n’a pas grand chose à voir avec ces problèmes-là. :)

Édité par nohar

I was a llama before it was cool

+1 -0

Je n’ai vraisemblablement pas l’expérience pour juger de la réelle utilité de ces outils.

C’est une impression, sur les forums ou autres lieux où peut-être seul débutants et amateurs s’expriment, ces annotations sont souvent (ou juste parfois alors) le reflet d’une conception qui ne s’accorde pas avec le langage.

Je devrais simplement relativiser cette Idéfix… :/

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte