803 votes

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

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

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

Le bloc optionnel "flèche" était absent de Python 2 et je n'ai pu trouver aucune information concernant sa signification dans Python 3. Il s'avère que c'est un Python correct et qu'il est accepté par l'interpréteur :

def f(x) -> 123:
    return x

J'ai pensé que ça pouvait être une sorte de syntaxe de précondition, mais.. :

  • Je ne peux pas tester x ici, car il n'est pas encore défini,
  • Peu importe ce que je mets après la flèche (par ex. 2 < 1 ), cela n'affecte pas le comportement de la fonction.

Quelqu'un qui a l'habitude de ce style de syntaxe pourrait-il l'expliquer ?

609voto

katrielalex Points 40655

C'est un annotation de fonction .

Plus précisément, Python 2.x dispose de docstrings, qui vous permettent d'attacher une chaîne de métadonnées à divers types d'objets. C'est incroyablement pratique, aussi Python 3 étend cette fonctionnalité en vous permettant d'attacher des métadonnées aux fonctions décrivant leurs paramètres et valeurs de retour.

Il n'y a pas de cas d'utilisation préconçu, mais le PEP en suggère plusieurs. L'un d'entre eux, 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.

166 votes

Et l'information est disponible sous forme de .__annotations__ attribut.

12 votes

Wow, j'ai raté un domaine de connaissances assez large - non seulement les annotations de valeurs de retour, mais aussi les annotations de paramètres. Merci beaucoup :).

6 votes

@Krotton Je ne peux pas vous blâmer de l'avoir manqué, il est pratiquement inutilisé. Je n'ai rencontré qu'une seule bibliothèque qui les utilise, et elle est assez obscure.

488voto

dawg Points 26051

Il s'agit d'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 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 de caractères :

>>> 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 bien, 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)

Imprimés

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

11 votes

Je pense que c'est un exemple complet.

240voto

MaxiMouse Points 2671

Dans le code suivant :

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

el -> int dit juste que f() renvoie un nombre entier (mais il ne force pas la fonction à renvoyer un nombre entier). On l'appelle un retour de l'annotation et on peut y accéder comme suit 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 devrait être un float . On y accède en tant que f.__annotations__['x'] et n'a pas de 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/

121voto

Jim Points 8793

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

PEP 3107 -- Annotations de fonctions décrit la spécification, en définissant les changements de grammaire, 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 -- Conseils sur les types y attache un sens unique : -> est utilisé pour indiquer le type que la fonction renvoie. Il semble également que cela sera appliqué dans les futures versions, comme décrit dans le document Qu'en est-il des utilisations existantes des annotations :

Le schéma le plus rapide envisageable consisterait à introduire une dépréciation silencieuse des annotations sans indice de type dans la version 3.6, une dépréciation complète dans la version 3.7, et une dépréciation complète dans la version 3.7. déclarer les indications de type comme la seule utilisation autorisée des annotations dans Python 3.8.

(C'est moi qui souligne)

Cela n'a pas encore été mis en œuvre à ce jour 3.6 pour autant que je puisse le dire, il pourrait être ajouté aux futures versions.

D'après ceci, l'exemple que vous avez fourni :

def f(x) -> 123:
    return x

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

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 aucun cas utilisées par Python lui-même, il les remplit et les ignore. C'est aux bibliothèques tierces de les utiliser.

0 votes

Bonne réponse. Juste par curiosité, les utilisez-vous dans votre code ?

0 votes

11voto

Vitalii Points 49

Cela signifie le type de résultat que la fonction renvoie, mais cela peut être None .

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

Par exemple, il y a dans le code de la bibliothèque pandas-profiling dans 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.

0 votes

"Cela signifie le type de résultat que la fonction renvoie, mais cela peut être Aucun." Il peut être None ou tout autre type.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X