ARTICLE AD BOX
First of all, these aliases have been deprecated since 3.9. For 3.9+ code, just use tuple[...] instead of using typing.Tuple[...].
These aliases aren't types at runtime. They "work" because well-formed code won't use them in a way that needs them to be types, while static type checkers don't care about what happens at runtime.
>>> assert not isinstance(typing.Tuple, type)They behave like instance objects since their constructors take arguments
They don't, actually. They don't even have constructors. The constructor to _SpecialGenericAlias takes arguments, as does the constructor to _BaseGenericAlias. But those aren't aliases themselves. They're superclasses of typing.Tuple, typing.List, and the other built-in aliases.
>>> typing.Tuple() # TypeError: Type Tuple cannot be instantiated; use tuple() insteadThe pseudo-exception to this is parameterized user-defined generics, which are callable. Even then, however, Spam[object] isn't itself a type; it's an instance of typing._GenericAlias. Calling Spam[object](bar) just forwards the call to Spam.__init__ behind the scenes.
But they also behave like class objects: they satisfy isinstance() of a different class (for example (1,2,3) is a tuple but isinstance((1,2,3), Tuple) is also true
That's because _SpecialGenericAlias overrides __subclasscheck__:
class _SpecialGenericAlias(_NotIterable, _BaseGenericAlias, _root=True): ... def __subclasscheck__(self, cls): if isinstance(cls, _SpecialGenericAlias): return issubclass(cls.__origin__, self.__origin__) if not isinstance(cls, _GenericAlias): return issubclass(cls, self.__origin__) return super().__subclasscheck__(cls) ...and _BaseGenericAlias overrides __instancecheck__ to use __subclasscheck__:
class _BaseGenericAlias(_Final, _root=True): ... def __instancecheck__(self, obj): return self.__subclasscheck__(type(obj)) ...This is basically just a hack; it changes the behavior of isinstance to use self.__origin__ (which represents the concrete type corresponding to the alias) instead of self (which is "supposed" to be a class but, for these aliases, isn't). Also, it only works on non-parameterized aliases. isinstance(object(), typing.Tuple[object] will throw an error.
Python type checkers (PyLance) accepts somevar : Tuple as a valid type
That's the whole point of the aliases. They don't do much at runtime; all of their power is in letting static type checkers pretend that they're types and handle static analysis accordingly.
