I was playing around new Python typing system and I faced concept which I don't know how to express in it. I want to pass the generic class type itself. E.g. to describe function which takes generic[1] G
, type T
and returns G[T]
.
I used mypy
to check verify my tries.
The naive approach didn't work in Python 3.7.
from typing import Generic, Type, List, Mapping, TypeVar
T = TypeVar('T')
def foo(generic: Generic, param: Type[T]) -> Generic[T]: # error: Invalid type "typing.Generic"
return generic[param] # error: Value of type "Generic?[T?]" is not indexable
ListOfInts: Type = foo(List, int)
assert ListOfInts == List[int] # passes in runtime
Naive GenericMeta in Python 3.6's backport of typing
?
There is GenericMeta is available in backport, but I didn't find how to use it and it didn't make it to PEP.
def foo(generic: GenericMeta, param: Type[T]) -> Type[Type[T]]:
return generic[param] # error: Value of type "GenericMeta" is not indexable
Mapping[Type, Type]?
I came up with Mapping
, but apparently List
is not such.
def apply(generic: Mapping[Type, Type], param: Type[T]) -> Type[Type[T]]:
return generic[param]
# error: Argument 1 to "apply" has incompatible type "Type[List[Any]]"; expected "Mapping[Type[Any], Type[Any]]"
ListOfInts: Type = apply(List, int)
Less strict works in Python 3.6 and 3.7
This is version I use for now.
It catches some absurd calls like apply(10, int)
, but apply(int, int)
will slip through static analysis and raise on runtime.
def apply(generic: Type, param: Type[T]) -> Type[Type[T]]:
return generic[param]
So the question comes is it possible to express generic type itself?
As for me, based on duck-typing, List
should simply be Mapping[Type, Type]
. I found PEP for protocols, maybe this is right tool for my problem?
[1] If I understand category theory right, such generic's type is technically named functor and is well known concept in languages like Haskell.