Les annotations en Python 3


Mais oui, on va parler un peu plus de Python 3 à partir de maintenant \o/

C’est qu’avec la prochaine version 3.4 qui apporte tellement de bonnes choses, Django 1.6 pleinement compatible et des libs comme Pillow et six pour combler les trous, ça y est, on peut enfin s’y mettre.

Commençons donc par les annotations, feature simple, et qu’on ne retrouve pas backportée en 2.7.

D’abord, à quoi servent les annotations ?

A rien !

Non, non, je ne déconne pas. Non seulement les annotations sont parfaitement optionnelles, mais en plus elles n’ont aucun effet.

Je répète pour que ça rentre : vous n’avez pas à utiliser les annotations. Aucun programme Python n’a besoin d’utiliser les annotations. Vous pouvez programmer votre vie entière en Python sans avoir jamais à utiliser les annotations. Et si vous les utilisez, elles ne changeront rien à votre programme.

Point.

Non, mais sérieux, ça sert à quoi ?

Python est un langage très souple, et une raison de sa souplesse c’est que la nature des données que vous traitez est dynamiquement découverte à l’exécution. Particulièrement, le type des variables est dynamique. La portée des variable est dynamique.

Avantages :

  • Le code est court et clair.
  • Le langage est très facile à apprendre.
  • On peut utiliser le duck typing.

Inconvénients :

  • Il faut écrire des informations en plus dans la doc.
  • Le code est plus lent car la VM ne peut optimiser le code pour les types.
  • Les IDE ont moins d’outils pour aider à la rédaction du code.

C’est la nature de Python. Pour certains cas, c’est ce qu’on veut. Dans d’autres cas où ce n’est pas adapté, il faut choisir un autre langage.

Les annotations sont là pour corriger ce point : on va permettre de fournir des informations en plus, si, et seulement, si, on le souhaite.

Ces informations n’auront aucun impact sur l’exécution du code. La VM Python va les ignorer. Mais un outil extérieur pourra potentiellement les utiliser pour en faire quelque chose.

Show me the code !

Faites chauffer votre interpréteur de Python 3…

On peut annoter uniquement les paramètres d’une fonction (ou méthode) :

>>> def ma_fonction(param1: "Une annotation", param2: 1 + 1, param3: int):
...     print(param1, param2, param3)
...
>>> ma_fonction(1,2,3)  # une annotation ne change absolument RIEN
1 2 3

L’annotation se note donc ainsi : nom du paramètre: annotation.

L’annotation PEUT être n’importe quelle expression Python valide. En fait l’annotation DOIT être une expression valide. Et cette expression sera exécutée une, et une seule fois, à l’initialisation du module. Le résultat sera stocké et accessible, via l’attribut __annotations__ :

>>> ma_fonction.__annotations__
{'param1': 'Une annotation', 'param2': 2, 'param3': <class 'int'>}

Évidément, les annotations se mélangent sans problème avec les cas avancés de paramétrage :

>>> def ma_fonction(param1 : "Une annotation" = "valeur par défaut", *param2: "Autre annotation"):
... print(param1, param2)
...
>>> ma_fonction()
valeur par défaut ()

On peut également spécifier une annotation pour la valeur de retour :

>>> def ma_fonction() -> None: # un petit goût de coffeescript
...     pass
...
>>> ma_fonction.__annotations__
{'return': None}

Et combiner tout ce bordel pour faire des trucs bien compliqués qui vous garantissent la sécurité d’emploi, ce qui manque cruellement en Python (les trucs compliqués, pas la sécurité d’emploi…):

>>> def ma_fonction(param1: lambda x: x * 2,
...                 param2: (lambda x: x * 2)(2) = 5,
...                 **params : "Keywords parameters rocks") -> list:
...     res = [param1, param2]
...     res.extend(params)
...     return res
...
>>> ma_fonction(1, 2, param3=3, param4=4)
[1, 2, 'param4', 'param3']
>>> ma_fonction.__annotations__
{'return': <class 'list'>, 'param1': <function <lambda> at 0x7f77a2982e60>, 'param2': 4, 'params': 'Keywords parameters rocks'}

Les lambdas ne peuvent pas être annotées par contre.

Le potentiel des annotations

Les annotations sont le cas typique d’une fonctionnalité bac à sable. En clair, sur la mailling list il y a eu de nombreuses requêtes pour permettre de la vérification de type, de l’auto documentation, de la translittération et autres joyeusetés.

Ne sachant pas comment répondre de manière propre à ces demandes, ni lesquelles étaient les plus prioritaires, les annotations ont été créées. Le but et de laisser maintenant la communauté fabriquer les outils, et ainsi :

  • Ceux pour les fonctionnalités les plus utilisées seront naturellement priorisées.
  • Les standards pour les annotations vont émerger par l’usage, et pas parce que Guido l’a décidé depuis son bureau au doigt mouillé.
  • Les fonctionnalités du genre “je veux un poney” seront tout simplement ignorées puisque personne ne travaillera suffisamment dessus (quand quelque chose est un caprice, le capricieux se sort rarement les extrémités manuelles de la cavité rectale.)

Des outils comme pyanno donnaient déjà un exemple de ce que la communauté pouvait désirer. Reste à savoir si ce sera vraiment utilisé. Les gens qui font du Python sont rarement fan du Java. Mais le caractère optionnel de la chose pourrait bien ajouter un vrai plus au langage sans l’alourdir.

Voici donc ce qu’on peut attendre des outils tierces parties qui utiliseront les annotations dans le futur :

Amélioration des performances

L’utilisation de RPython pour coder Pypy a montré que les perfs de Python pouvaient être fantastiques, si on rajoutait quelques annotations. RPython, est, je le rappelle, un subset de Python (donc tout code RPython tourne dans l’interpréteur Python normal), mais avec des annotations de type, sous forme de commentaires.

On peut donc imaginer que dans le futur, les codes en fort besoin de perfs comme pour l’embarqué ou le traitement scientifique, pourront ajouter dans les parties clés de l’algo des annotations pour booster la vitesse ou diminuer la mémoire consommée. On parlerait ici alors d’annotation de type. int, list, UneClasse sont en effet des expressions Python valides…

L’avantage de l’annotation, c’est son caractère purement optionnel, qu’on peut ajouter seulement à certains codes, ou à une partie du code.

Aide à la saisie

Les IDE ont du mal avec Python. La complétion du code, l’aide en popup, la refactorisation automatique et toutes ces joyeusetés sont rarement bien implémentées.

Dans ma vie de tous les jours, je m’en branle. Je code tellement vite en Python, je ne m’en aperçois même pas. Mais quand j’utilise une nouvelle lib, ça peut être utile.

Typiquement, pour un code comme celui de 0bin, je ne me servirais pas des annotations. Mais pour une lib comme minibelt, je prendrais peut être le temps de mettre des annotations de type, pour aider ceux qui vont l’utiliser. Ou une annotation pour signaler les exceptions que certaines fonctions peuvent lever.

Génération de documentation

Certains voient les annotations comme un complément aux docstrings. Ce n’est pas mon cas, mais je vous le note, au cas où ça devienne une tendance.

Vérification de Type

Dans la même lignée de l’aide à la saisie, pour certaines fonctions (c’est rare, mais ça arrive), on veut éviter le duck typing et forcer l’utilisateur à passer un type en particulier. Avoir cette possibilité via les annotations pourraient être utile.

Je suis septique sur cet usage, en effet des libs existent depuis des années pour faire cela, et plus, via des décorateurs, et ça n’est pas très utilisé. La différence est qu’un mécanisme standard pourra être détecté par les IDE.

Après on risque d’avoir des abus pour ce genre de fonctionnalités. Je vois d’ici arriver les codeurs d’autres langages caféinés, essayant de pousser l’usage d’une annotation rendant des attributs private :-)

Heureusement, les annotations seront normalement pour toujours optionnelles.

Translittération de code

Vous avez un code Python et vous voulez le transformer en code Cobol ? Bon, le compilateur de Pypy a déjà le potentiel de le faire, mais avec les annotations, il est potentiellement possible de rajouter suffisamment d’informations pour qu’on puisse toujours transformer un code de Python vers un autre langage Turing complet.

J’ai dit potentiellement.

Je répète une dernière fois…

Les annotations ne font rien. Elles n’ont pas le but de faire quelque chose et de toute façon sont optionnelles.

Leur but est de permettre à la communauté de créer des nouveaux outils utilisant les annotations et voir lesquels seront les plus utilisés, matures et désirés.

Pour le moment, il n’y a pas grand chose de fait avec, mais jusqu’ici Python 3 n’a pas été très populaire. Cela risque de changer avec 2014 qui propulsera Python 3.4 dans la cour des grands.

15 thoughts on “Les annotations en Python 3

  • Syl

    Salut Sam,

    Quand tu parle d’annotations “de type”, l’idée est de s’assurer qu’un paramètre fourni est bien de tel ou tel type?

    Mais pourquoi ne pas passer directement par un “assert isinstance(int, myvar)”, qui oblige carrément à fournir CE type?

    Pour les docstrings, le ne vois pas non plus ce qu’on pourrait ajouter à la syntaxe RST, qui est déjà très complète.

    Mais bon, on s’en fout, puisque c’est optionnel!

    Sinon, j’ai pas compris pour le “bureau au doigt mouillé” de Guido…j’ai cherché le lien avec Google, Dropbox, python…mais lapin compris!

  • kontre

    Il existe des librairies pour compiler des fonctions python à la volée (par exemple numba ou cython), et qui soit détectent automatiquement les types, soit utilisent les types données par l’utilisateur. On peut imaginer qu’il pourraient utiliser les annotations pour ça plutôt que des décorateurs persos.

    @Syl: “au doigt mouillé” ça veut dire au pif. Et Guido est assis à son bureau.

  • Syl

    @kontre: merci pour l’info!
    S&M est aussi un excellent site pour amléiorer sa culture générale! ^^

  • Sekigo

    Bonjour.

    Pour le duck-typing, il ne sera pas “cassé” si on utilise intelligemment la vérification de type via une structure (à l’aide du module ABC) comme celle-ci:

    # -*- coding: utf-8 -*-
    from __future__ import unicode_literals, print_function, division
    from abc import ABCMeta, abstractmethod
     
    class Canard(object):
        __metaclass__ = ABCMeta
     
        @abstractmethod
        def coin_coin(self):
            return "coin_coin !"
     
        @classmethod
        def __subclasshook__(cls, C):
            if cls is Canard:
                if any("coin_coin" in B.__dict__ for B in C.__mro__):
                    return True
            return NotImplemented
     
     
    class Caneton(Canard):
        def coin_coin(self):
            return "piou-piou !"
     
     
    class Chat(object):
        def coin_coin(self):
            return "miaou-miaou !"
     
     
    def vieux_bigleux_qui_jette_bout_de_pain(canard):
        assert isinstance(canard, Canard)
        print(canard.coin_coin())
     
     
    vieux_bigleux_qui_jette_bout_de_pain(Caneton())
    vieux_bigleux_qui_jette_bout_de_pain(Chat())

    Désolé, c’est un peu brut comme code (et c’est du python2.7), mais on peut imaginer vérifier le type canard via une instance. L’important étant qu’il ait les bonnes méthodes, qu’il soit réellement un canard, on s’en fout.
    (J’espère que le code va être correctement indenté…)

  • Sam Post author

    Ce qui suppose qu’on utilise du ABC partout, ce qui est fort relou. Python n’est pas Java.

  • swordofpain

    Donc, si je suis bien, ça veut dire qu’il serait possible d’utiliser un décorateur pour faire de la validation de type automatique si on annote les types des paramètres ? Ça peut être sympa en effet :–)

  • Xavier Combelle

    Si je me trompe pas RPython n’utilise pas les annotation, mais un sous ensemble de python suffisamment simple pour que l’inférence de type fonctionne.

  • Sam Post author

    Après check, tu as raison. Je sais pas pourquoi j’ai clairement cette image des comments utiliser pour le typing. Je dois mixer deux projets, mais alors retrouver d’où ça vient…

  • JeromeJ

    T’es sûr qu’il faut un espace après les “:” et avant l’annotation ?

    Ça me paraît dégueu mais c’est peut-être moi qui me trompe, dans ce cas là, je serais déçu ! C’est mouche comme ça!

  • Sam Post author

    Il n’y a pas de règle syntaxique sur les espaces qui soient rendues obligatoires par le langage.

    Le PEP8 lui-même ne donne pas de règle mais :

    The Python standard library will not use function annotations as that would result in a premature commitment to a particular annotation style. Instead, the annotations are left for users to discover and experiment with useful annotation styles.

    C’est une fonctionnalité bac à sable, ne l’oublions pas.

  • looper

    Pouvoir utiliser les annotations dans les boucles for serait utile pour l’auto-complétion (quand on connait pas par coeur une lib… un IDE ça aide, sauf là)

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.