Annotations de Type pour * args et * * kwargs
j'essaie les annotations de type de Python avec des classes de base abstraites pour écrire quelques interfaces. Est-il possible d'annoter les types possibles de *args
et **kwargs
?
par exemple, comment pourrait-on exprimer que les arguments sensés d'une fonction sont soit un int
ou deux int
s? type(args)
donne Tuple
donc à mon avis était d'Annoter le type comme Union[Tuple[int, int], Tuple[int]]
, mais ça ne marche pas.
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))
messages D'erreur 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]]"
Il il est logique que mypy n'aime pas cela pour l'appel de fonction car il s'attend à ce qu'il y ait un tuple
dans l'appel lui-même. L'addition après déballage donne aussi une erreur de frappe que je ne comprends pas.
comment annoter les types sensibles pour *args
et **kwargs
?
3 réponses
pour les arguments de position variables (*args
) et les arguments de mots-clés variables (**kw
) il vous suffit de spécifier la valeur attendue pour tel argument.
listes D'arguments arbitraires et valeurs d'arguments par défautType De Conseils PEP:
les listes D'arguments arbitraires peuvent aussi bien être annotées, de sorte que la définition:
def foo(*args: str, **kwds: int): ...
est acceptable et cela signifie que, par exemple, tous les appels de fonctions suivants représentent des types d'arguments valides:
foo('a', 'b', 'c') foo(x=1, y=2) foo('', z=0)
alors vous voudriez spécifier votre méthode comme ceci:
def foo(*args: int):
cependant, si votre fonction ne peut accepter qu'une ou deux valeurs entières, vous ne devez pas utiliser *args
utilisez un seul argument de position explicite et un second argument de mot clé:
def foo(first: int, second: Optional[int] = None):
Maintenant, votre fonction est en fait limitée à un ou deux arguments, et les deux doit être entier si spécifié. *args
toujours signifie 0 ou plus, et ne peut pas être limité par des indices de type à une plage plus spécifique.
en bref ajout à la réponse précédente, si vous essayez d'utiliser mypy sur des fichiers Python 2 et que vous avez besoin d'utiliser des commentaires pour ajouter des types au lieu d'annotations, vous devez préfixer les types pour args
et kwargs
*
et **
respectivement:
def foo(param, *args, **kwargs):
# type: (bool, *str, **int) -> None
pass
ceci est traité par mypy comme étant la même que la version Python 3.5 de foo
:
def foo(param: bool, *args: str, **kwargs: int) -> None:
pass
La bonne façon de le faire est d'utiliser @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))
notez que vous n'ajoutez pas @overload
ou tapez les annotations à la mise en œuvre réelle, qui doit venir en dernier.
Vous aurez besoin d'un newish version des deux typing
et mypy pour obtenir de l'aide pour @surcharge en dehors de fichiers stub.
vous pouvez également utiliser ceci pour modifier le résultat retourné d'une manière qui rend explicite quels types d'arguments correspondent avec quel type de retour. par exemple:
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))