J'ai récemment remarqué quelque chose d'intéressant en regardant spécification grammaticale Python 3. :
funcdef: 'def' NAME parameters ['->' test] ':' suite
Le bloc facultatif 'arrow' était absent de Python 2 et je n'ai trouvé aucune information sur sa signification dans Python 3. Il s'avère que c'est correct Python et c'est accepté par l'interprète:
def f(x) -> 123:
return x
Je pensais que cela pourrait être une sorte de syntaxe de précondition, mais:
x
ici, à ce qui n'est pas encore défini,2 < 1
), cela n’affecte pas le comportement de la fonction.Quelqu'un qui est habitué à cette syntaxe pourrait-il l'expliquer?
C'est un annotation de fonction .
De manière plus détaillée, Python 2.x dispose de docstrings, qui vous permettent d'attacher une chaîne de métadonnées à divers types d'objet. Ceci 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 leurs valeurs de retour.
Il n'y a pas de cas d'utilisation préconçu, mais le PEP en suggère plusieurs. Une très pratique consiste à 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 coince les arguments dans le bon type. Une autre consiste à autoriser une documentation spécifique à un paramètre au lieu de l'encoder dans la chaîne de documentation.
Ce sont des annotations de fonctions 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 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 les 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.__
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>
Comme d'autres réponses l'ont indiqué, le symbole ->
est utilisé dans les annotations de fonctions. Dans les versions plus récentes de Python >= 3.5
, cependant, il a une signification définie .
PEP 3107 - Annotations de fonctions décrit la spécification, définissant les modifications de grammaire, l'existence de func.__annotations__
dans lequel elles sont stockées et le fait que son cas d'utilisation est toujours ouvert.
Dans Python 3.5
cependant, PEP 484 - Indications de type attache une signification unique à ceci: ->
est utilisé pour indiquer le type renvoyé par la fonction . Il semble également que cela sera appliqué dans les versions futures, 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 sans indication de type dans 3.6, une dépréciation totale en 3.7 et déclarerait les indications de type comme la seule utilisation autorisée des annotations dans Python 3.8.
(Souligné par moi)
Autant que je sache, cela n'a pas encore été implémenté à partir de 3.6
, de sorte que les versions futures risquent de l'être.
Selon cela, 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 faudrait changer pour:
def f(x) -> int:
return x
pour qu'il décrive efficacement cette fonction f
renvoie un objet de type int
.
Les annotations ne sont en aucune manière utilisées par Python, il les remplit et les ignore. C'est aux bibliothèques tierces de travailler avec elles.
Python l'ignore. Dans le code suivant:
def f(x) -> int:
return int(x)
le -> int
indique simplement que f()
renvoie un entier. Cela 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)
: float
indique aux personnes qui lisent le programme (et à certaines bibliothèques/programmes tiers, par exemple, pylint) que x
doit être un float
. On y accède en tant que f.__annotations__['x']
, et n’a aucune signification en soi. Voir la documentation pour plus d'informations:
https://docs.python.org/3/reference/compound_stmts.html#function-definitionshttps://www.python.org/dev/peps/pep-3107/
def function(arg)->123:
C'est simplement un type de retour, entier dans ce cas, le nombre que vous écrivez importe peu.
comme Java :
public int function(int args){...}
Mais pour Python (comment Jim Fasarakis Hilliard dit) le type de retour est juste un indice , donc il suggère le retour mais permet quand même de renvoyer un autre type comme une chaîne.