¿Cómo acceder a los argumentos de tipo de typing.Generic?

El módulo de typing proporciona una clase base para sugerencias de tipo genérico: la clase typing.Generic .

Las subclases de Generic aceptan argumentos de tipo entre corchetes, por ejemplo:

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

Mi pregunta es, ¿cómo puedo acceder a estos argumentos de tipo?

Es decir, dado str_to_bool_dict como entrada, ¿cómo puedo obtener str y bool como salida?

Básicamente estoy buscando una función tal que

 >>> magic_function(str_to_bool_dict) (, ) 

Posibilidad 1

A partir de Python 3.6. hay un campo público __args__ y ( __parameters__ ). Por ejemplo:

 print( typing.List[int].__args__ ) 

Esto contiene los parámetros generics (es decir, int ), mientras que __parameters__ contiene el genérico en sí (es decir, ~T ).

Posibilidad 2

Utilice typing_inspect.getargs

Cual usar

typing sigue PEP8 . Tanto PEP8 como la typing son coautoras de Guido van Rossum. Un subrayado doble inicial y final se define como “objetos o atributos” mágicos “que viven en espacios de nombres controlados por el usuario .

Los dunders también se comentan en línea; desde el repository oficial para escribir podemos ver: * __args__ es una tupla de todos los argumentos utilizados en las suscripciones, por ejemplo, Dict[T, int].__args__ == (T, int) “.

Sin embargo, los autores también notan : * “El módulo de escritura tiene un estado provisional, por lo que no está cubierto por los altos estándares de compatibilidad con versiones anteriores (aunque intentamos mantenerlo tanto como sea posible), esto es especialmente cierto para (aún sin documentar) dunder atributos como __union_params__ . Si desea trabajar con tipos de escritura en el contexto de tiempo de ejecución, entonces puede estar interesado en el proyecto typing_inspect (parte del cual puede terminar escribiendo más adelante).

En general, todo lo que haga con la typing tendrá que mantenerse actualizado por el momento. Si necesita reenviar cambios compatibles, le recomendaría que escriba sus propias clases de anotación.

Parece que este método interno hará el truco.

 typing.List[int]._subs_tree() 

que devuelve la tupla:

 (typing.List, ) 

Pero esta es una API privada, probablemente hay una mejor respuesta.

Por lo que sé, no hay una respuesta feliz aquí.

Lo que viene a la mente es el atributo __args__ indocumentado que almacena esta información:

 list_of_ints.__args__ >>>(,) str_to_bool_dict.__args__ >>>(, ) 

pero no se menciona en la documentación del módulo de typing .

Vale la pena señalar que estaba muy cerca de ser mencionado en la documentación, sin embargo:

Probablemente también deberíamos discutir si necesitamos documentar todos los argumentos de palabras clave para GenericMeta.__new__ . Hay tvars , args , origin , extra y orig_bases . Creo que podríamos decir algo sobre los tres primeros (corresponden a __parameters__ , __args__ , y __origin__ y estos son utilizados por la mayoría de las cosas al escribir).

Pero no lo consiguió del todo .

GenericMeta a __all__ y agregué cadenas de documentación a GenericMeta y GenericMeta.__new__ luego de la discusión en el tema. Decidí no describir a __origin__ y amigos en cadenas de documentos. En su lugar, acabo de agregar un comentario en el lugar donde se usan por primera vez.

A partir de ahí, todavía tienes tres opciones que no se excluyen mutuamente:

  • espere a que el módulo de typing scope su plena madurez y espere que estas características se documenten pronto

  • únase a la lista de correo de ideas de Python y vea si se puede obtener suficiente apoyo para hacer que estos elementos internos sean públicos / parte de la API

  • Mientras tanto, trabaje con las partes internas indocumentadas, apostando a que no habrá cambios en estos o que los cambios serán menores.

Tenga en cuenta que el tercer punto difícilmente se puede evitar ya que incluso la API puede estar sujeta a cambios :

El módulo de escritura se ha incluido en la biblioteca estándar de forma provisional. Se pueden agregar nuevas características y la API puede cambiar incluso entre versiones menores si los desarrolladores centrales lo consideran necesario .

Usa el .__args__ en tus construcciones. Así que la función mágica que necesitas es algo como:

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

Mi pregunta es, ¿cómo puedo acceder a estos argumentos de tipo?

En situaciones como estas, ¿cómo puedo acceder …

Usa las poderosas características de introspección de Python.

Incluso como progtwigdor no profesional, sé que estoy tratando de inspeccionar cosas y dir es una función que es como IDE en el terminal. Así que después

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

Quiero ver si hay algo que haga la magia que quieres.

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

Veo demasiada información, para ver si estoy en lo correcto verifico

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

Ahora encontremos los métodos que fueron diseñados específicamente para tipos generics

 >>> 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__'} 

entre estos, __parameters__ , __extra__ , __args__ y __origin__ suenan útiles. __extra__ y __origin__ no funcionarán sin uno mismo, por lo que nos quedamos con __parameters__ y __args__ .

 >>> str_to_bool_dict.__args__ (, ) 

De ahí la respuesta.


La introspección permite que las declaraciones de afirmación de py.test hagan que los marcos de prueba derivados de JUnit parezcan obsoletos. Incluso los lenguajes como JavaScript / Elm / Clojure no tienen una aplicación directa como Python. La convención de nomenclatura de Python le permite descubrir el idioma sin leer realmente (documentando en algunos casos como estos) las documentaciones.

Así que busque utilizando la introspección y lea la documentación / listas de correo para confirmar sus hallazgos.

PS a OP: este método también responde a su pregunta ¿Cuál es la forma correcta de verificar si un objeto está escribiendo? ¿Genérico? use discovery si no puede comprometerse con la lista de correo o si es un desarrollador ocupado, esa es la forma de hacerlo en Python.