There is no supported API for this. Under limited circumstances, if you’re willing to mess around with undocumented implementation details, you can sometimes do it, but it’s not reliable at all.
First, mypy doesn’t require you to provide type arguments when assigning to a generically-typed variable. You can do things like
x: Test[int] = Test() and neither Python nor mypy will complain. mypy infers the type arguments, but
Test is used at runtime instead of
Test[int]. Since explicit type arguments are awkward to write and carry a performance penalty, lots of code only uses type arguments in the annotations, not at runtime.
There’s no way to recover type arguments at runtime that were never provided at runtime.
When type arguments are provided at runtime, the implementation does currently try to preserve this information, but only in a completely undocumented internal attribute that is subject to change without notice, and even this attribute might not be present. Specifically, when you call
, the class of the new object is
Test rather than
Test[int], but the
typing implementation attempts to set
obj.__orig_class__ = Test[int]
on the new object. If it cannot set
__orig_class__ (for example, if
__slots__), then it catches the AttributeError and gives up.
__orig_class__ was introduced in Python 3.5.3; it is not present on 3.5.2 and lower. Nothing in
typing makes any actual use of
The timing of the
__orig_class__ assignment varies by Python version, but currently, it’s set after normal object construction has already finished. You will not be able to inspect
These implementation details are current as of CPython 3.8.2.
__orig_class__ is an implementation detail, but at least on Python 3.8, you don’t have to access any additional implementation details to get the type arguments. Python 3.8 introduced
typing.get_args, which returns a tuple of the type arguments of a
typing type, or
() for an invalid argument. (Yes, there was really no public API for that all the way from Python 3.5 until 3.8.)
typing.get_args(Test[int]().__orig_class__) == (int,)
__orig_class__ is present and you’re willing to access it, then
get_args together provide what you’re looking for.