460 votes

Annotations de type pour * args et ** kwargs

Je suis d'essayer Python annotations de type avec les classes de base abstraites à écrire des interfaces. Est-il possible d'annoter les types possibles d' *args et **kwargs?

Par exemple, comment pourrait-on exprimer que le sensible, les arguments d'une fonction sont soit une int ou deux ints? type(args) donne Tuple donc je pense à annoter le type 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))

Les 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 la fonction, car il s'attend à être 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 le sensible types de *args et **kwargs?

472voto

Martijn Pieters Points 271458

Pour la variable d'arguments de position (*args) et de la variable d'arguments mots-clefs (**kw) vous avez seulement besoin de spécifier la valeur attendue pour un tel argument.

De l' Arbitraire des listes d'arguments et de la valeur par défaut des arguments de la section des préconisations de la PEP:

Arbitraire des listes d'arguments peuvent ainsi être de type annoté, de sorte que la définition:

def foo(*args: str, **kwds: int): ...

qui est acceptable et ce qui signifie que, par exemple, tous les éléments suivants représentent les appels de fonction en cours de validité avec les types d'arguments:

foo('a', 'b', 'c')
foo(x=1, y=2)
foo('', z=0)

Si vous voulez préciser votre méthode comme ceci:

def foo(*args: int):

Toutefois, si votre fonction ne peut accepter une ou deux valeurs de type entier, vous ne devez pas utiliser *args , l'utilisation explicite de position argument et un deuxième argument 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 doivent être des entiers si spécifié. *args toujours signifie 0 ou plus, et ne peut pas être limitée par le type de conseils plus spécifiques de la gamme.

54voto

chadrik Points 81

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'avez pas à ajouter de l' @overload ou les annotations de type pour la mise en œuvre réelle, qui doit être le 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 l'utiliser pour faire varier le résultat retourné de façon explicite dont l'argument types correspondent avec laquelle le 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))

28voto

Michael0x2a Points 9116

En guise de bref ajout à 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 et kwargs avec * et ** respectivement:

 def foo(param, *args, **kwargs):
    # type: (bool, *str, **int) -> None
    pass
 

Ceci est traité par mypy comme étant le même que celui ci-dessous, version Python 3.5 de foo :

 def foo(param: bool, *args: str, **kwargs: int) -> None:
    pass
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X