8 votes

Dans les interfaces Into et From : luttant avec la covariance du type de typing.

Je suis en train de mettre en place une interface pour les conversions entre les types, mais j'ai du mal à la rendre cohérente car typing.Type est covariant

U = TypeVar('U')

class Into(Protocol[U]):
    @abstractmethod
    def into(self, t: Type[U]) -> U:
        pass

La documentation donne un exemple similaire avec une différence cruciale

class Utilisateur: ...
class UtilisateurBasique(Utilisateur): ...
class UtilisateurPro(Utilisateur): ...
class UtilisateurEquipe(Utilisateur): ...

def creer_nouvel_utilisateur(classe_utilisateur: Type[Utilisateur]) -> Utilisateur:
    return classe_utilisateur()

Là, ils disent que les vérificateurs de type devraient vérifier que toutes les sous-classes de Utilisateur doivent implémenter un constructeur avec une signature valide pour être instanciées de cette façon. Mon cas d'utilisation est différent car je pourrais ne pas construire le nouveau type, mais simplement retourner un type préexistant. Disons que je fais

class X: pass

class Enveloppe:
    def __init__(self, x: X):
        self._x = x

    def into(self, t: Type[X]) -> X:
        return self._x

tout fonctionne bien jusqu'à ce que quelqu'un crée une sous-classe de X

w = Enveloppe(X())
...
class XX(X): pass
x: XX = w.into(XX)

Le Côté droit est bon selon mypy car Type est covariant, mais clairement l'API est cassée car un X n'est pas un XX. Si Type n'était pas covariant, cela ne serait pas un problème : le Côté droit ne passerait pas la vérification du type tant que Enveloppe n'était pas mise à jour pour supporter XX.

Ma question est : y a-t-il un moyen d'atteindre cela (ou quelque chose de similaire) étant donné la covariance de Type?

Contexte

Je veux utiliser ceci pour convertir un type en plusieurs autres types, en spécifiant le type désiré de manière explicite plutôt que simplement vers_X, vers_Y, etc. Je m'attends à le faire avec TypeVar ou surcharger. J'ai également des difficultés ici.

Cela est inspiré par le Into de Rust, où t: Type[U] est un paramètre de type et non un argument de fonction.

-2voto

Kate Melnykova Points 1595

eval résout le problème.

class Wrapper:
    def __init__(self, x: X):
        self._x = x

    def into(self, t: Type[X]) -> X:
        return eval(f'{t}({self._x})')

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