¿Qué significa - > en las definiciones de funciones de Python?


Recientemente he notado algo interesante al mirar la especificación gramatical de Python 3.3 :

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

El bloque opcional 'arrow' estaba ausente en Python 2 y no pude encontrar ninguna información sobre su significado en Python 3. Resulta que esto es Python correcto y es aceptado por el intérprete:

def f(x) -> 123:
    return x

Pensé que esto podría ser algún tipo de sintaxis de precondición, pero:

  • No puedo probar x aquí, en que todavía es undefined,
  • No importa lo que ponga después de la flecha (por ejemplo, 2 < 1), no afecta el comportamiento de la función.

¿Podría alguien acostumbrado a esta sintaxis explicarlo?

Author: Jim Fasarakis Hilliard, 2013-01-17

3 answers

Es una anotación de función .

En más detalle, Python 2.x tiene docstrings, que le permiten adjuntar una cadena de metadatos a varios tipos de objetos. Esto es increíblemente práctico, por lo que Python 3 amplía la función al permitirle adjuntar metadatos a las funciones que describen sus parámetros y valores de retorno.

No hay ningún caso de uso preconcebido, pero el PEP sugiere varios. Una muy útil es permitirle anotar parámetros con sus tipos esperados; entonces sea fácil escribir un decorador que verifique las anotaciones o coaccione los argumentos al tipo correcto. Otra es permitir documentación específica de parámetros en lugar de codificarla en docstring.

 171
Author: Katriel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-17 13:04:57

Estas son anotaciones de funciones cubiertas en PEP 3107. Específicamente, el -> marca la anotación de la función de retorno.

Ejemplos:

>>> 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'}

Las anotaciones son diccionarios, así que puedes hacer esto:

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

También puede tener una estructura de datos de python en lugar de solo una cadena:

>>> 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'

O bien, puede usar atributos de función para validar valores llamados:

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)

Impresiones

>>> 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
Author: dawg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-07 12:31:57

Como han indicado otras respuestas, el símbolo -> se usa como parte de las anotaciones de funciones. En versiones más recientes de Python >= 3.5, sin embargo, tiene un significado definido.

PEP 3107 Annot Function Annotations describe la especificación, definiendo los cambios gramaticales, la existencia de func.__annotations__ en el que se almacenan y, el hecho de que su caso de uso todavía está abierto.

En Python 3.5 sin embargo, PEP 484 Hints Type Hints asigna un único significado a esto: -> se usa para indicar el tipo que devuelve la función. También parece que esto se aplicará en futuras versiones como se describe en ¿Qué pasa con los usos existentes de las anotaciones :

El esquema más rápido concebible introduciría la desaprobación silenciosa de anotaciones que no sean de tipo en 3.6, la desaprobación completa en 3.7, y declararía las sugerencias de tipo como el único uso permitido de anotaciones en Python 3.8.

(Énfasis mine)

Esto no ha sido realmente implementado a partir de 3.6 por lo que puedo decir, por lo que podría ser golpeado a futuras versiones.

De acuerdo con esto, el ejemplo que ha proporcionado:

def f(x) -> 123:
    return x

Será prohibido en el futuro (y en las versiones actuales será confuso), tendría que ser cambiado a:

def f(x) -> int:
    return x

Para describir efectivamente esa función f devuelve un objeto de tipo int.

Las anotaciones no son usadas de ninguna manera por Python en sí mismo, prácticamente los pueblan e ignoran. Es hasta 3rd bibliotecas de partido para trabajar con ellos.

 24
Author: Jim Fasarakis Hilliard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-06 16:30:30