Estoy probando las anotaciones de tipo de Python con clases base abstractas para escribir algunas interfaces. ¿Hay alguna manera de anotar los posibles tipos de *args
y **kwargs
?
Por ejemplo, ¿cómo se podría expresar que los argumentos razonables para una función son una int
o dos int
s? type(args)
da, Tuple
así que mi suposición fue anotar el tipo como Union[Tuple[int, int], Tuple[int]]
, pero esto no funciona.
from typing import Union, Tuple
def foo(*args: Union[Tuple[int, int], Tuple[int]]):
try:
i, j = args
return i + j
except ValueError:
assert len(args) == 1
i = args[0]
return i
# ok
print(foo((1,)))
print(foo((1, 2)))
# mypy does not like this
print(foo(1))
print(foo(1, 2))
Mensajes de error de mypy:
t.py: note: In function "foo":
t.py:6: error: Unsupported operand types for + ("tuple" and "Union[Tuple[int, int], Tuple[int]]")
t.py: note: At top level:
t.py:12: error: Argument 1 to "foo" has incompatible type "int"; expected "Union[Tuple[int, int], Tuple[int]]"
t.py:14: error: Argument 1 to "foo" has incompatible type "int"; expected "Union[Tuple[int, int], Tuple[int]]"
t.py:15: error: Argument 1 to "foo" has incompatible type "int"; expected "Union[Tuple[int, int], Tuple[int]]"
t.py:15: error: Argument 2 to "foo" has incompatible type "int"; expected "Union[Tuple[int, int], Tuple[int]]"
Tiene sentido que a mypy no le guste esto para la llamada a la función porque espera que haya una tuple
en la llamada misma. La adición después de desempacar también da un error de tipeo que no entiendo.
¿Cómo se anotan los tipos sensibles para *args
y **kwargs
?
fuente
Optional
? ¿Cambió algo sobre Python o cambiaste de opinión? ¿Todavía no es estrictamente necesario debido alNone
valor predeterminado?Optional
anotación implícita automática cuando se usaNone
como valor predeterminado hizo que ciertos casos de uso fueran más difíciles y ahora se está eliminando de la PEP.Optional
que se requerirá explícitamente en el futuro.Callable
no admite ninguna mención de una sugerencia de tipo*args
o punto**kwargs
final . Ese problema específico se trata de marcar incrustaciones que aceptan argumentos específicos más un número arbitrario de otros y, por lo tanto*args: Any, **kwargs: Any
, usan una sugerencia de tipo muy específica para los dos términos generales. Para los casos en los que establece*args
y / o**kwargs
algo más específico, puede usar aProtocol
.La forma correcta de hacer esto es usar
@overload
Tenga en cuenta que no agrega
@overload
ni escribe anotaciones a la implementación real, que debe ser la última.Necesitará una versión más reciente de ambos
typing
y mypy para obtener soporte para @overload fuera de los archivos stub .También puede usar esto para variar el resultado devuelto de manera que haga explícito qué tipos de argumento corresponden con qué tipo de retorno. p.ej:
fuente
(type1)
vs(type1, type1)
llamadas a funciones como mi ejemplo. Quizás(type1)
vs(type2, type1)
hubiera sido un mejor ejemplo y muestra por qué me gusta esta respuesta. Esto también permite diferentes tipos de retorno. Sin embargo, en el caso especial en el que solo tiene un tipo de retorno y su*args
y*kwargs
todos son del mismo tipo, la técnica en la respuesta de Martjin tiene más sentido, por lo que ambas respuestas son útiles.*args
donde hay un número máximo de argumentos (2 aquí) sigue siendo incorrecto .*args
necesariamente mal aquí? Si las llamadas esperadas eran(type1)
vs(type2, type1)
, entonces el número de argumentos es variable y no hay un valor predeterminado apropiado para el argumento final. ¿Por qué es importante que haya un máximo?*args
está realmente allí para cero o más argumentos sin límites, homogéneos, o para "pasarlos por todos". Tiene un argumento requerido y uno opcional. Eso es totalmente diferente y normalmente se maneja dando al segundo argumento un valor predeterminado de centinela para detectar que se omitió.*args
, una respuesta aún mejor a la pregunta es que esto no es algo que deba hacerse en absoluto.Como una breve adición a la respuesta anterior, si está tratando de usar mypy en archivos de Python 2 y necesita usar comentarios para agregar tipos en lugar de anotaciones, debe prefijar los tipos para
args
ykwargs
con*
y**
respectivamente:Mypy trata esto como si fuera la misma versión de Python 3.5 de abajo
foo
:fuente