¿Qué significa -> en las definiciones de funciones de Python?
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
x
aquí, 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?
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.
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>
En el siguiente código:
def f(x) -> int:
return int(x)
simplemente -> int
dice 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)
: float
le dice a las personas que leen el programa (y algunas bibliotecas/programas de terceros, por ejemplo, pylint) que x
deberí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/
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.5
tiene 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.6
por 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 f
devuelve 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.