The typing
module provides a base class for generic type hints: The typing.Generic
class.
Subclasses of Generic
accept type arguments in square brackets, for example:
list_of_ints = typing.List[int] str_to_bool_dict = typing.Dict[str, bool]
My question is, how can I access these type arguments?
That is, given str_to_bool_dict
as input, how can I get str
and bool
as output?
Basically I'm looking for a function such that
>>> magic_function(str_to_bool_dict) (<class 'str'>, <class 'bool'>)
The actual type arguments of a generic type are. reference types, wildcards, or. parameterized types (i.e. instantiations of other generic types).
The aim of generics are to: Allow functions, methods and classes to work with arguments of any type whilst maintaining the information on the relationships between things, such as arguments and return values.
Generic functions: from typing import TypeVar, Sequence T = TypeVar('T') # Declare type variable def first(seq: Sequence[T]) -> T: return seq[0] def last(seq: Sequence[T]) -> T: return seq[-1] n = first([1, 2, 3]) # n has type int.
A generic function is composed of multiple functions implementing the same operation for different types. Which implementation should be used during a call is determined by the dispatch algorithm. When the implementation is chosen based on the type of a single argument, this is known as single dispatch.
As of Python3.8 there is typing.get_args
:
print( get_args( List[int] ) ) # (<class 'int'>,)
PEP-560 also provides __orig_bases__[n]
, which allows us the arguments of the nth generic base:
from typing import TypeVar, Generic, get_args T = TypeVar( "T" ) class Base( Generic[T] ): pass class Derived( Base[int] ): pass print( get_args( Derived.__orig_bases__[0] ) ) # (<class 'int'>,)
As of Python 3.6. there is a public __args__
and (__parameters__
) field. For instance:
print( typing.List[int].__args__ )
This contains the generic parameters (i.e. int
), whilst __parameters__
contains the generic itself (i.e. ~T
).
Use typing_inspect.getargs
typing
follows PEP8. Both PEP8 and typing
are coauthored by Guido van Rossum. A double leading and trailing underscore is defined in as: "“magic” objects or attributes that live in user-controlled namespaces".
The dunders are also commented in-line; from the official repository for typing we can see:
__args__
is a tuple of all arguments used in subscripting, e.g., Dict[T, int].__args__ == (T, int)
".However, the authors also note:
__union_params__
. If you want to work with typing types in runtime context, then you may be interested in the typing_inspect
project (part of which may end up in typing later)."I general, whatever you do with typing
will need to be kept up-to-date for the time being. If you need forward compatible changes, I'd recommend writing your own annotation classes.
As far as I know, there is no happy answer here.
What comes to mind is the __args__
undocumented attribute which stores this information:
list_of_ints.__args__ >>>(<class 'int'>,) str_to_bool_dict.__args__ >>>(<class 'str'>, <class 'bool'>)
but there is no mention of it in the documentation of the typing
module.
It is worth noting that it was very close to be mentioned in the documentation though:
Probably we should also discuss whether we need to document all keyword arguments for
GenericMeta.__new__
. There aretvars
,args
,origin
,extra
, andorig_bases
. I think we could say something about first three (they correspond to__parameters__
,__args__
, and__origin__
and these are used by most things in typing).
But it did not quite make it:
I added
GenericMeta
to__all__
and added docstrings toGenericMeta
andGenericMeta.__new__
following the discussion in the issue. I decided not to describe__origin__
and friends in docstrings. Instead, I just added a comment at the place where they are first used.
From there, you still have three non-mutually exclusive options:
wait for the typing
module to reach full maturity and hope these features will be documented soon
join the Python ideas mailing list and see if enough support can be gathered to make these internals public / part of the API
work in the meantime with the undocumented internals, making a gamble that there won't be changes to these or that the changes will be minor.
Note that the third point can hardly be avoided as even the API can be subject to changes:
The typing module has been included in the standard library on a provisional basis. New features might be added and API may change even between minor releases if deemed necessary by the core developers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With