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

477

J'ai récemment remarqué quelque chose d'intéressant en regardant la spécification de grammaire Python 3.3 :

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

Le bloc optionnel «flèche» é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 il est accepté par l'interpréteur:

def f(x) -> 123:
    return x

J'ai pensé que cela pourrait être une sorte de syntaxe de condition préalable, mais:

  • Je ne peux pas tester xici, 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 habitué à cette syntaxe pourrait-il l'expliquer?

Krotton
la source

Réponses:

376

C'est une annotation de fonction .

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

Il n'y a pas de cas d'utilisation préconçu, mais le PEP en propose plusieurs. Un très pratique est de vous permettre d'annoter les paramètres avec leurs types attendus; il serait alors 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 la docstring.

Katriel
la source
122
Et les informations sont disponibles en tant .__annotations__qu'attribut.
Martijn Pieters
8
Wow, j'ai raté un domaine de connaissances assez large - non seulement des annotations de valeur de retour, mais aussi des annotations de paramètres. Merci beaucoup :).
Krotton
4
@Krotton Je ne peux pas vous reprocher de l'avoir manqué, il est pratiquement inutilisé. Je n'ai rencontré qu'une seule bibliothèque en les utilisant, et c'est assez obscur.
5
Et l' __annotations__attribut est un dictionnaire. La clé returnest celle utilisée pour récupérer la valeur après la flèche.
Keith
9
@delnan - probablement la raison pour laquelle il est principalement inutilisé parce que la plupart des bibliothèques python visent toujours à être compatibles avec python2.x. Alors que python3.x commence à devenir plus standard, nous pourrions voir plus de ces choses apparaître ici et là ...
mgilson
253

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, vous pouvez donc le faire:

>>> '{:,} {}'.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)

Impressions

>>> f(2,2) 
AssertionError: x==2; Test: _between: must be between 3 and 10
>>> f(3,2.1)
AssertionError: y==2.1; Test: <lambda>
dawg
la source
87

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

PEP 3107 - Les annotations de fonction décrivaient la spécification, définissant les changements de grammaire, l'existence func.__annotations__dans laquelle ils sont stockés et le fait que son cas d'utilisation soit toujours ouvert.

En Python 3.5cependant, PEP 484 - Type Hints attache une seule signification à ceci: ->est utilisé pour indiquer le type retourné 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 la dépréciation silencieuse des annotations sans indication de type en 3.6, la dépréciation complète en 3.7 et déclarerait les indications de type comme la seule utilisation autorisée des annotations en Python 3.8.

(Souligner le mien)

Cela n'a pas été réellement implémenté pour 3.6autant que je sache, il pourrait donc être transféré vers les versions futures.

Selon cela, l'exemple que vous avez fourni:

def f(x) -> 123:
    return x

sera interdite à l'avenir (et dans les versions actuelles sera source de confusion), il faudrait la changer en:

def f(x) -> int:
    return x

pour qu'il décrive efficacement cette fonction fretourne 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 à peu près. C'est aux bibliothèques tierces de travailler avec elles.

Dimitris Fasarakis Hilliard
la source
65

Dans le code suivant:

def f(x) -> int:
    return int(x)

le -> intjuste indique que f()renvoie un entier (mais cela ne force pas la fonction à renvoyer un entier). Elle s'appelle une annotation de retour et est accessible en tant que f.__annotations__['return'].

Python prend également en charge les annotations de paramètres:

def f(x: float) -> int:
    return int(x)

: floatindique aux personnes qui lisent le programme (et certaines bibliothèques / programmes tiers, par exemple pylint) que cela xdevrait être a float. Il est accessible en tant que f.__annotations__['x'], et n'a aucune signification en soi. Consultez la documentation pour plus d'informations:

https://docs.python.org/3/reference/compound_stmts.html#function-definitions https://www.python.org/dev/peps/pep-3107/

MaxiMouse
la source
4

Cela signifie le type de résultat renvoyé par la fonction, mais il peut être None .

Il est répandu dans les bibliothèques modernes orientées sur Python 3.x.

Par exemple, il y a dans le code de bibliothèque pandas-profiling en de nombreux endroits par exemple:

def get_description(self) -> dict:

def get_rejected_variables(self, threshold: float = 0.9) -> list:

def to_file(self, output_file: Path or str, silent: bool = True) -> None:
"""Write the report to a file.
Vitalii
la source
"Cela signifie le type de résultat renvoyé par la fonction, mais il peut s'agir de Aucun." Il peut s'agir de Aucun ou de tout autre type.
Ebram Shehata
2

def function(arg)->123:

C'est simplement un type de retour, entier dans ce cas, peu importe le nombre que vous écrivez.

comme Java :

public int function(int args){...}

Mais pour Python (comment Jim Fasarakis Hilliard l'a dit), le type de retour est juste un indice , donc c'est suggérer le retour mais permet quand même de renvoyer un autre type comme une chaîne ..

Mike D3ViD Tyson
la source
1
def f(x) -> 123:
    return x

Mon résumé:

  1. Simplement ->est introduit pour que les développeurs spécifient éventuellement le type de retour de la fonction. Voir la proposition d'amélioration Python 3107

  2. Ceci est une indication de la façon dont les choses peuvent évoluer à l'avenir alors que Python est largement adopté - une indication vers un typage fort - c'est mon observation personnelle.

  3. Vous pouvez également spécifier des types d'arguments. La spécification du type de retour des fonctions et des arguments aidera à réduire les erreurs logiques et à améliorer les améliorations du code.

  4. Vous pouvez avoir des expressions comme type de retour (à la fois au niveau de la fonction et du paramètre) et le résultat des expressions est accessible via l'attribut 'return' de l'objet annotations . les annotations seront vides pour l'expression / valeur de retour pour les fonctions en ligne lambda.

maz
la source
Merci pour les corrections.
maz