Que signifie -> dans les définitions de fonctions Python?

J'ai récemment remarqué quelque chose d'intéressant lorsque l'on regarde Python 3.3 spécification de la grammaire:

funcdef: 'def' NAME parameters ['->' test] ':' suite

Le bloc 'arrow' facultatif était absent dans Python 2 et je n'ai trouvé aucune information concernant sa signification dans Python 3. Il s'avère que C'est Python correct et qu'il est accepté par l'interpréteur:

def f(x) -> 123:
    return x

Je pensais que cela pourrait être une sorte de syntaxe préalable, mais:

  • je ne peux pas tester x ici, c'est encore indéfini,
  • peu importe ce que je mets après la flèche (par exemple 2 < 1), cela n'affecte pas le comportement de la fonction.

Quelqu'un d'habitué à cette syntaxe pourrait-il l'expliquer?

220
demandé sur Jim Fasarakis Hilliard 2013-01-17 17:03:35

3 réponses

C'est une annotation de fonction .

Plus en détail, Python 2.x a docstrings, qui vous permettent d'attacher une chaîne de métadonnées à différents types d'objets. C'est incroyablement pratique, donc Python 3 étend la fonctionnalité en vous permettant d'attacher des métadonnées aux fonctions décrivant leurs paramètres et leurs valeurs de retour.

Il N'y a pas de cas d'utilisation préconçus, mais le PEP en suggère plusieurs. Un très maniable un est de vous permettre d'annoter les paramètres avec leurs attendus; il serait alors il est facile d'écrire un décorateur qui vérifie les annotations ou contraint les arguments au bon type. Une autre consiste à autoriser la documentation spécifique aux paramètres au lieu de l'encoder dans docstring.

171
répondu Katriel 2013-01-17 13:04:57

Ce sont des annotations de fonction couvertes dans PEP 3107 . Plus précisément, le -> Marque l'annotation de la fonction de retour.

Exemples:

>>> def kinetic_energy(m:'in KG', v:'in M/S')->'Joules': 
...    return 1/2*m*v**2
... 
>>> kinetic_energy.__annotations__
{'return': 'Joules', 'v': 'in M/S', 'm': 'in KG'}

Les Annotations sont des dictionnaires, donc vous pouvez faire ceci:

>>> '{:,} {}'.format(kinetic_energy(20,3000),
      kinetic_energy.__annotations__['return'])
'90,000,000.0 Joules'

Vous pouvez également avoir une structure de données python plutôt qu'une simple chaîne:

>>> rd={'type':float,'units':'Joules','docstring':'Given mass and velocity returns kinetic energy in Joules'}
>>> def f()->rd:
...    pass
>>> f.__annotations__['return']['type']
<class 'float'>
>>> f.__annotations__['return']['units']
'Joules'
>>> f.__annotations__['return']['docstring']
'Given mass and velocity returns kinetic energy in Joules'

Ou, vous pouvez utiliser des attributs de fonction pour valider les valeurs appelées:

def validate(func, locals):
    for var, test in func.__annotations__.items():
        value = locals[var]
        try: 
            pr=test.__name__+': '+test.__docstring__
        except AttributeError:
            pr=test.__name__   
        msg = '{}=={}; Test: {}'.format(var, value, pr)
        assert test(value), msg

def between(lo, hi):
    def _between(x):
            return lo <= x <= hi
    _between.__docstring__='must be between {} and {}'.format(lo,hi)       
    return _between

def f(x: between(3,10), y:lambda _y: isinstance(_y,int)):
    validate(f, locals())
    print(x,y)

Imprime

>>> f(2,2) 
AssertionError: x==2; Test: _between: must be between 3 and 10
>>> f(3,2.1)
AssertionError: y==2.1; Test: <lambda>
116
répondu dawg 2015-05-07 12:31:57

Comme d'autres réponses l'ont indiqué, le symbole -> est utilisé dans le cadre des annotations de fonctions. Dans les versions plus récentes de Python >= 3.5, cependant, il a une signification définie .

PEP 3107 -- Function Annotations décrit la spécification, définissant les changements grammaticaux, l'existence de func.__annotations__ dans lequel ils sont stockés et, le fait que son cas d'utilisation est toujours ouvert.

En Python 3.5 cependant, PEP 484 -- Type Hints attache une seule signification à this: {[2] } est utilisé pour indiquer le type renvoyé par la fonction. Il semble également que cela sera appliqué dans les futures versions comme décrit dans qu'en est-il des utilisations existantes des annotations :

Le schéma le plus rapide concevable introduirait une dépréciation silencieuse des annotations non-type-hint dans 3.6, une dépréciation complète dans 3.7, et déclarerait les indices de type comme la seule utilisation autorisée des annotations dans Python 3.8.

(accent le mien)

Cela n'a pas été réellement implémenté à partir de 3.6 autant que je sache, donc il pourrait être heurté aux futures versions.

Selon ceci, l'exemple que vous avez fourni:

def f(x) -> 123:
    return x

Sera interdit à l'avenir (et dans les versions actuelles sera source de confusion), il devrait être changé en:

def f(x) -> int:
    return x

Pour décrire efficacement cette fonction f renvoie un objet de type int.

Les annotations ne sont en aucun cas utilisées par Python lui-même, il les remplit et les ignore. C'est aux bibliothèques 3ème partie de travailler avec eux.

24
répondu Jim Fasarakis Hilliard 2017-07-06 16:30:30