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

Resuelto Krotton asked hace 11 años • 11 respuestas

Recientemente noté algo interesante al observar la especificación gramática de Python 3.3 :

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

El bloque opcional 'flecha' 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 el intérprete lo acepta:

def f(x) -> 123:
    return x

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

  • No puedo probar xaquí, ya que aún no está definido,
  • No importa lo que ponga después de la flecha (por ejemplo 2 < 1), no afecta el comportamiento de la función.

¿Alguien familiarizado con este estilo de sintaxis podría explicarlo?

Krotton avatar Jan 17 '13 20:01 Krotton
Aceptado

Es una anotación de función .

Más detalladamente, Python 2.x tiene cadenas de documentación, que le permiten adjuntar una cadena de metadatos a varios tipos de objetos. Esto es increíblemente útil, por lo que Python 3 amplía la función permitiéndole adjuntar metadatos a funciones que describen sus parámetros y valores de retorno.

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

Katriel avatar Jan 17 '2013 13:01 Katriel

Estas son anotaciones de funciones cubiertas en PEP 3107 . Específicamente, ->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(12,30),
      kinetic_energy.__annotations__['return'])
'5,400.0 Joules'

También puedes 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 puede usar atributos de función para validar los 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)

Huellas dactilares

>>> 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 avatar Feb 25 '2013 17:02 dawg

En el siguiente código:

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

simplemente -> intdice que f()devuelve un número entero (pero no obliga a la función a devolver un número entero). Se llama anotación de retorno y se puede acceder a ella como f.__annotations__['return'].

Python también admite anotaciones de parámetros:

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

: floatle dice a las personas que leen el programa (y algunas bibliotecas/programas de terceros, por ejemplo, pylint) que xdebería ser un archivo float. Se accede a él como f.__annotations__['x']y no tiene ningún significado por sí solo. Consulte la documentación para obtener más información:

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

Maximouse avatar May 06 '2019 09:05 Maximouse

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

PEP 3107: Anotaciones de funciones describieron la especificación, definiendo los cambios gramaticales, la existencia del func.__annotations__lugar donde se almacenan y el hecho de que su caso de uso aún está abierto.

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

El esquema más rápido imaginable introduciría una desaprobación silenciosa de las anotaciones sin sugerencias de tipo en 3.6, una desaprobación total en 3.7 y declararía las sugerencias de tipo como el único uso permitido de anotaciones en Python 3.8.

(El énfasis es mío)

Hasta donde yo sé, esto no se ha implementado, 3.6por lo que podría pasar a versiones futuras.

De acuerdo con esto, el ejemplo que ha proporcionado:

def f(x) -> 123:
    return x

estará prohibido en el futuro (y en las versiones actuales será confuso), sería necesario cambiarlo a:

def f(x) -> int:
    return x

para que describa efectivamente esa función fdevuelve un objeto de tipo int.

Python no utiliza las anotaciones de ninguna manera, prácticamente las completa e ignora. Depende de las bibliotecas de terceros trabajar con ellos.