23 votes

Typage Python : déclarer le type de valeur de retour en fonction de l'argument de la fonction

Supposons que j'ai une fonction qui prend un type comme argument et renvoie une instance de ce type :

def fun(t):
    return t(42)

Je peux alors l'appeler et obtenir des objets des types fournis :

fun(int)           # 42
fun(float)         # 42.0
fun(complex)       # (42+0j)
fun(str)           # "42"
fun(MyCustomType)  # something

Cette liste n'est pas exhaustive, j'aimerais pouvoir utiliser n'importe quel type avec un constructeur approprié.

Ensuite, j'aimerais ajouter des indications de type pour cette fonction. Quel devrait être l'indice de type pour la valeur de retour de cette fonction ?


J'ai essayé en utilisant simplement t comme t est un type :

def fun(t: type) -> t:
    return t(42)

mais ça ne marche pas :

main.py:1 : erreur : Le nom 't' n'est pas défini


Cette réponse suggère en utilisant un TypeVar :

from typing import TypeVar

T = TypeVar("T")

def fun(t: T) -> T:
    return t(42)

Mais cela ne semble pas être juste, car T désigne un type, ce qui suggère que le type lui-même est retourné, et non son instance. Mypy le rejette :

main.py:6 : erreur : "object" not callable


Utilisation de Any ça marche évidemment, mais je trouve que c'est trop vague, ça ne traduit pas l'intention :

from typing import Any

def fun(t: type) -> Any:
    return t(42)

15voto

MisterMiyagi Points 2734

TLDR : Vous avez besoin d'un TypeVar para le type de retour de l'appel t :

def fun(t: Callable[[int], R]) -> R:
    ...

La contrainte d'un type est trop restrictive ici. La fonction accepte n'importe quel Callable qui prend un entier, et le type de retour de la fonction est celui de la fonction Callable . Cela peut être spécifié en utilisant un TypeVar pour le type de retour :

from typing import Callable, TypeVar

R = TypeVar('R')  # the variable return type

def fun(t: Callable[[int], R]) -> R:
    return t(42)

fun(int)                            # Revealed type is 'builtins.int*'
fun(float)                          # Revealed type is 'builtins.float*'
reveal_type(fun(lambda x: str(x)))  # Revealed type is 'builtins.str*'

Cela fonctionne également pour les types, car l'instanciation d'un type est un appel.

Si une signature plus complexe, par exemple avec des arguments de type mot-clé, est nécessaire, utilisez Protocol (de typing o typing_extensions ).


Notez que si on veut explicitement passer seulement 42 à la Callable , Literal (de typing o typing_extensions ) peut être utilisé pour le préciser.

R = TypeVar('R')

def fun(t: Callable[[Literal[42]], R]) -> R:
    return t(42)

Notez que toute fonction de type Callable[[int], R] satisfait également Callable[[Literal[42]], R] .

11voto

juanpa.arrivillaga Points 35811

Vous êtes à la recherche de typing.Type donc quelque chose comme ça :

from typing import TypeVar, Type

T = TypeVar("T", str, complex, float, int)

def fun(t: Type[T]) -> T:
    return t(42)

fun(int)
fun(float)
fun(complex)
fun(str)

Notez que votre variable de type doit être contrainte, car toutes les Type acceptent les arguments, mais vous pouvez les limiter à quelques-uns comme dans votre exemple.

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