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

477

Recientemente noté algo interesante al mirar la especificación gramatical 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 este 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 previa, pero:

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

¿Podría alguien acostumbrado a esta sintaxis explicarlo?

Krotton
fuente

Respuestas:

376

Es una anotación de función .

Con más detalle, Python 2.x tiene cadenas de documentos, 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 característica al permitirle adjuntar metadatos a funciones que describen sus parámetros y valores de retorno.

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

Katriel
fuente
122
Y la información está disponible como un .__annotations__atributo.
Martijn Pieters
8
Wow, me perdí un área bastante amplia de conocimiento, no solo las anotaciones de valor de retorno, sino también las anotaciones de parámetros. Muchas gracias :).
Krotton
44
@Krotton No puedo culparte por perderte, prácticamente no se usa. Solo conocí una sola biblioteca usándolos, y es bastante oscuro.
55
Y el __annotations__atributo es un diccionario. La clave returnes la utilizada para recuperar el valor después de la flecha.
Keith el
99
@delnan: probablemente la razón por la que no se usa en su mayoría es porque la mayoría de las bibliotecas de Python todavía apuntan a ser compatibles con python2.x. A medida que python3.x comienza a ser más estándar, podríamos ver más de estas cosas apareciendo aquí y allá ...
mgilson
253

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, por lo que puede 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 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>
perro
fuente
87

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

PEP 3107 - Las anotaciones de funciones describieron la especificación, definieron los cambios gramaticales, la existencia func.__annotations__en la que están almacenados y el hecho de que su caso de uso aún está abierto.

Sin 3.5embargo, en Python , PEP 484 - Type Hints le da un significado único 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é hay de los usos existentes de las anotaciones :

El esquema concebible más rápido introduciría la desaprobación silenciosa de las anotaciones sin sugerencia 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.

(El énfasis es mío)

Esto no se ha implementado realmente 3.6hasta donde puedo decir, por lo que podría verse afectado en futuras versiones.

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), debería cambiarse a:

def f(x) -> int:
    return x

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

Las anotaciones no son utilizadas de ninguna manera por Python en sí, sino que las llena e ignora. Depende de las bibliotecas de terceros trabajar con ellas.

Dimitris Fasarakis Hilliard
fuente
65

En el siguiente código:

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

el -> intsolo dice que f()devuelve un entero (pero no obliga a la función a devolver un entero). Se denomina anotación de retorno y se puede acceder 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ían ser a float. Se accede como f.__annotations__['x'], y no tiene ningún significado en sí mismo. Vea la documentación para más información:

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

MaxiMouse
fuente
4

Esto significa el tipo de resultado que devuelve la función, pero puede ser None.

Está muy extendido en las bibliotecas modernas orientadas a Python 3.x.

Por ejemplo, existe en el código de la biblioteca pandas-perfiles en muchos lugares, por ejemplo:

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
fuente
"Esto significa el tipo de resultado que devuelve la función, pero puede ser Ninguno". Puede ser Ninguno o cualquier otro tipo.
Ebram Shehata
2

def function(arg)->123:

Es simplemente un tipo de retorno, entero en este caso no importa qué número escriba.

como Java :

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

Pero para Python (como dijo Jim Fasarakis Hilliard ) el tipo de retorno es solo una pista , por lo que sugiere el retorno pero permite de todos modos devolver otro tipo como una cadena ...

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

Mi resumen:

  1. Simplemente ->se presenta para que los desarrolladores especifiquen opcionalmente el tipo de retorno de la función. Ver la propuesta de mejora de Python 3107

  2. Esta es una indicación de cómo se pueden desarrollar las cosas en el futuro a medida que Python se adopte ampliamente, una indicación hacia una escritura fuerte, esta es mi observación personal.

  3. También puede especificar tipos para argumentos. Especificar el tipo de retorno de las funciones y argumentos ayudará a reducir los errores lógicos y mejorar las mejoras de código.

  4. Puede tener expresiones como tipo de retorno (tanto a nivel de función como de parámetro) y se puede acceder al resultado de las expresiones a través del atributo 'return' del objeto de anotaciones . las anotaciones estarán vacías para el valor de expresión / retorno para las funciones en línea lambda.

maz
fuente
Gracias por las correcciones.
maz