J'essaie les annotations de type Python avec des classes de base abstraites pour écrire certaines interfaces. Existe-t-il un moyen d'annoter les types possibles de *args
et **kwargs
?
Par exemple, comment exprimerait-on que les arguments sensibles d'une fonction sont soit un int
ou deux int
s? type(args)
donne Tuple
donc ma supposition était d'annoter le type comme Union[Tuple[int, int], Tuple[int]]
, mais cela ne fonctionne 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 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'ajout après le déballage donne également une erreur de frappe que je ne comprends pas.
Comment annoter les types sensibles pour *args
et **kwargs
?
la source
Optional
? Quelque chose a changé à propos de Python ou avez-vous changé d'avis? N'est-il toujours pas strictement nécessaire en raison duNone
défaut?Optional
annotation implicite automatique lorsque vous utilisezNone
comme valeur par défaut a rendu certains cas d'utilisation plus difficiles et qui est maintenant supprimée du PEP.Optional
dans le futur.Callable
ne prend en charge aucune mention d'un indice de type*args
ou d'**kwargs
un point . Ce problème spécifique concerne le marquage des callables qui acceptent des arguments spécifiques plus un nombre arbitraire d'autres , et donc utilisent*args: Any, **kwargs: Any
, un indice de type très spécifique pour les deux fourre-tout. Pour les cas où vous définissez*args
et / ou**kwargs
quelque chose de plus spécifique, vous pouvez utiliser unProtocol
.La bonne façon de faire est d'utiliser
@overload
Notez que vous n'ajoutez
@overload
ni ne tapez d'annotations à l'implémentation réelle, qui doit venir en dernier.Vous aurez besoin d'une nouvelle version des deux
typing
et de mypy pour obtenir la prise en charge de @overload en dehors des fichiers stub .Vous pouvez également l'utiliser pour faire varier le résultat renvoyé de manière à rendre explicite les types d'argument correspondant à quel type de retour. par exemple:
la source
(type1)
vs(type1, type1)
comme exemple. Peut-être que(type1)
vs(type2, type1)
aurait été un meilleur exemple et montre pourquoi j'aime cette réponse. Cela permet également différents types de retour. Cependant, dans le cas particulier où vous n'avez qu'un seul type de retour et votre*args
et*kwargs
sont tous du même type, la technique de la réponse de Martjin a plus de sens, donc les deux réponses sont utiles.*args
là où il y a un nombre maximum d'arguments (2 ici) est cependant toujours faux .*args
nécessairement faux ici? Si les appels attendus étaient(type1)
vs(type2, type1)
, alors le nombre d'arguments est variable et il n'y a pas de valeur par défaut appropriée pour l'argument de fin. Pourquoi est-il important qu'il y ait un maximum?*args
est vraiment là pour zéro ou plus , des arguments homogènes non plafonnés, ou pour «transmettre ces derniers intacts» fourre-tout. Vous avez un argument obligatoire et un facultatif. C'est totalement différent et est normalement géré en donnant au deuxième argument une valeur par défaut sentinelle pour détecter que ce dernier a été omis.*args
, une réponse encore meilleure à la question est que ce n'est pas quelque chose qui devrait être fait du tout.Pour compléter brièvement la réponse précédente, si vous essayez d'utiliser mypy sur des fichiers Python 2 et que vous devez utiliser des commentaires pour ajouter des types au lieu d'annotations, vous devez préfixer les types pour
args
etkwargs
avec*
et**
respectivement:Ceci est traité par mypy comme étant le même que la version Python 3.5 ci-dessous de
foo
:la source