font - subplot python title
Escriba anotaciones para*args y** kwargs (3)
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
y
kwargs
con
*
y
**
respectivamente:
def foo(param, *args, **kwargs):
# type: (bool, *str, **int) -> None
pass
Mypy trata esto como si fuera la misma versión de
foo
de Python 3.5 a continuación:
def foo(param: bool, *args: str, **kwargs: int) -> None:
pass
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
int
o dos
int
s?
type(args)
le da a
Tuple
así que supongo que 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
?
La forma correcta de hacer esto es usar
@overload
from typing import overload
@overload
def foo(arg1: int, arg2: int) -> int:
...
@overload
def foo(arg: int) -> int:
...
def foo(*args):
try:
i, j = args
return i + j
except ValueError:
assert len(args) == 1
i = args[0]
return i
print(foo(1))
print(foo(1, 2))
Tenga en cuenta que no agrega
@overload
ni escribe anotaciones en la implementación real, que debe ser la última.
Necesitará una versión más reciente de
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:
from typing import Tuple, overload
@overload
def foo(arg1: int, arg2: int) -> Tuple[int, int]:
...
@overload
def foo(arg: int) -> int:
...
def foo(*args):
try:
i, j = args
return j, i
except ValueError:
assert len(args) == 1
i = args[0]
return i
print(foo(1))
print(foo(1, 2))
Para argumentos posicionales variables (
*args
) y argumentos de palabras clave variables (
**kw
) solo necesita especificar el valor esperado para
uno de
esos argumentos.
Desde las listas de argumentos arbitrarios y la sección de valores de argumentos predeterminados de la sugerencia de tipo PEP:
Las listas de argumentos arbitrarios también pueden ser de tipo anotado, de modo que la definición:
def foo(*args: str, **kwds: int): ...
es aceptable y significa que, por ejemplo, todo lo siguiente representa llamadas a funciones con tipos válidos de argumentos:
foo(''a'', ''b'', ''c'') foo(x=1, y=2) foo('''', z=0)
Por lo tanto, desea especificar su método de esta manera:
def foo(*args: int):
Sin embargo, si su función solo puede aceptar uno o dos valores enteros, no debe usar
*args
, use un argumento de posición explícito y un segundo argumento de palabra clave:
def foo(first: int, second: Optional[int] = None):
Ahora su función está realmente limitada a uno o dos argumentos, y ambos deben ser enteros si se especifica.
*args
siempre
significa 0 o más, y no puede limitarse por sugerencias de tipo a un rango más específico.