web-dev-qa-db-fra.com

Comment accéder aux arguments de type de typing.Generic?

Le module typing fournit une classe de base pour les indicateurs de type génériques: Le typing.Generic class.

Les sous-classes de Generic acceptent les arguments de type entre crochets, par exemple:

list_of_ints = typing.List[int]
str_to_bool_dict = typing.Dict[str, bool]

Ma question est, comment puis-je accéder à ces arguments de type?

C’est-à-dire que, avec str_to_bool_dict en entrée, comment puis-je obtenir str et bool en sortie?

Fondamentalement, je recherche une fonction telle que

>>> magic_function(str_to_bool_dict)
(<class 'str'>, <class 'bool'>)
14
Aran-Fey

Possibilité 1

À partir de Python 3.6. il existe un champ public __args__ et (__parameters__) . Par exemple:

print( typing.List[int].__args__ )

Celui-ci contient les paramètres génériques (c'est-à-dire int), tandis que __parameters__ contient le générique lui-même (c'est-à-dire ~T). 

Possibilité 2

Utilisez typing_inspect.getargs

Lequel utiliser

typing suit PEP8 . PEP8 et typing sont tous deux co-créés par Guido van Rossum. Les traits de soulignement double interligne et fin sont définis comme suit: "objets ou attributs« magiques »qui résident dans les espaces de noms contrôlés par l'utilisateur".

Les dunders sont également commentés en ligne; depuis le référentiel officiel pour en tapant nous pouvons voir: * "__args__ est un tuple de tous les arguments utilisés en subscripting, par exemple, Dict[T, int].__args__ == (T, int)".

Cependant, le les auteurs notent également }: _: * "Le module de frappe a un statut provisoire, il n'est donc pas couvert par les normes élevées de compatibilité ascendante (bien que nous essayions de le conserver autant que possible). ), ceci est particulièrement vrai pour les attributs dunder (encore non documentés) tels que __union_params__. Si vous souhaitez travailler avec des types de frappe dans un contexte d'exécution, le projet typing_inspect vous intéressera peut-être (une partie de celle-ci pourrait être saisie ultérieurement) "

Général, tout ce que vous ferez avec typing devra être tenu à jour pour le moment. Si vous avez besoin de modifications compatibles en aval, je vous recommande d'écrire vos propres classes d'annotation.

13
c z

Il semble que cette méthode interne fera l'affaire

typing.List[int]._subs_tree()

qui retourne le tuple:

(typing.List, <class 'int'>)

Mais c'est une API privée, il y a probablement une meilleure réponse.

5
Petros Makris

Pour autant que je sache, il n'y a pas de réponse heureuse ici.

Ce qui me vient à l’esprit est l’attribut __args__ non documenté qui stocke ces informations:

list_of_ints.__args__
>>>(<class 'int'>,)

str_to_bool_dict.__args__
>>>(<class 'str'>, <class 'bool'>)

mais il n'en est pas fait mention dans la documentation du module typing.

Il est à noter que c'était très proche d'être mentionné dans la documentation cependant:

Nous devrions probablement également déterminer s'il est nécessaire de documenter tous les arguments de mot clé pour GenericMeta.__new__. Il existe tvars, args, Origin, extra et orig_bases. Je pense que nous pourrions dire quelque chose à propos des trois premiers (ils correspondent à __parameters__, __args__ et __Origin__ et ceux-ci sont utilisés par la plupart des choses en tapant).

Mais cela n’a pas vraiment réussi :

J'ai ajouté GenericMeta à __all__ et ajouté docstrings à GenericMeta et GenericMeta.__new__ à la suite de la discussion dans le numéro . J'ai décidé de ne pas décrire __Origin__ et d'amis dans docstrings. Au lieu de cela, j'ai simplement ajouté un commentaire à l'endroit où ils ont été utilisés pour la première fois.

À partir de là, vous avez encore trois options non mutuellement exclusives:

  • attendez que le module typing atteigne sa pleine maturité et espérez que ces fonctionnalités seront bientôt documentées

  • rejoindre la liste de diffusion idées Python et voir si un support suffisant peut être réuni pour rendre ces internals publics/une partie de l'API

  • travailler en attendant avec les internes non documentés, en faisant le pari qu'il n'y aura pas de changements à ces derniers ou que les changements seront mineurs.

Notez que le troisième point peut difficilement être évité car même l’API peut être soumis à des modifications :

Le module de dactylographie a été inclus dans la bibliothèque standard à titre provisoire. De nouvelles fonctionnalités peuvent être ajoutées et API peut changer même entre des versions mineures si les développeurs principaux le jugent nécessaire.

4
Jacques Gaudin

Utilisez le .__args__ sur vos constructions. La fonction magique dont vous avez besoin ressemble à quelque chose comme:

get_type_args = lambda genrc_type: getattr(genrc_type, '__args__')

Ma question est, comment puis-je accéder à ces arguments de type?

Dans de telles situations, comment puis-je accéder à ...

Utilisez les puissantes fonctionnalités d’introspection de Python.

Même en tant que programmeur non professionnel, je sais que j’essaie d’inspecter des éléments et que dir est une fonction qui ressemble à IDE dans le terminal. Donc après

>>> import typing
>>> str_to_bool_dict = typing.Dict[str, bool]

Je veux voir s'il y a quelque chose qui fait la magie que vous voulez afin

>>> methods = dir(str_to_bool_dict)
>>> methods
['__abstractmethods__', '__args__', .....]

Je vois trop d'informations, pour voir si je suis correct, je vérifie

>>> len(methods)
53
>>> len(dir(dict))
39

Maintenant, laissez-nous trouver les méthodes qui ont été conçues spécifiquement pour les types génériques

>>> set(methods).difference(set(dir(dict)))
{'__slots__', '__parameters__', '_abc_negative_cache_version', '__extra__',
'_abc_cache', '__args__', '_abc_negative_cache', '__Origin__',
'__abstractmethods__', '__module__', '__next_in_mro__', '_abc_registry',
'__dict__', '__weakref__'}

parmi ceux-ci, __parameters__, __extra__, __args__ et __Origin__ semblent utiles. __extra__ et __Origin__ ne fonctionneront pas sans moi, il nous reste donc __parameters__ et __args__.

>>> str_to_bool_dict.__args__
(<class 'str'>, <class 'bool'>)

D'où la réponse.


Introspection autorise les instructions assert de py.test à rendre les frameworks de test dérivés de JUnit obsolètes. Même les langages comme JavaScript/Elm/Clojure n’ont pas de fonction aussi simple que dir of Python. La convention de nommage de Python vous permet de découvrir la langue sans lire réellement (documentant dans certains cas comme celui-ci) les documentations.

Alors, recherchez l'introspection et lisez la documentation/les listes de diffusion pour confirmer vos conclusions.

P.S. To OP - cette méthode répond également à votre question Quelle est la bonne façon de vérifier si un objet est un typing.Generic? utilisez la découverte si vous ne pouvez pas vous inscrire à la liste de diffusion ou si vous êtes un développeur occupé - c’est la façon de le faire en python.

0
RinkyPinku