Я играл с новой системой типизации Python и столкнулся с концепцией, которую не знаю, как в ней выразить. Я хочу передать сам общий тип класса. Например. для описания функции, которая принимает универсальную[1] G
, набирает T
и возвращает G[T]
.
Я использовал mypy
, чтобы проверить мои попытки.
Наивный подход не работал в 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 в бэкпорте Python 3.6 для typing
?
GenericMeta доступна в backport, но я не нашел, как ее использовать, и она не не дойти до PEP.
def foo(generic: GenericMeta, param: Type[T]) -> Type[Type[T]]:
return generic[param] # error: Value of type "GenericMeta" is not indexable
Сопоставление[Тип, Тип]?
Я придумал Mapping
, но видимо List
не такой.
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)
Менее строго работает в Python 3.6 и 3.7
Это версия, которую я использую на данный момент. Он перехватывает некоторые абсурдные вызовы, такие как apply(10, int)
, но apply(int, int)
пропускается при статическом анализе и поднимается во время выполнения.
def apply(generic: Type, param: Type[T]) -> Type[Type[T]]:
return generic[param]
Итак, возникает вопрос, возможно ли выразить сам общий тип?
Что касается меня, то на основе утиного набора List
должно быть просто Mapping[Type, Type]
. Я нашел PEP для протоколов, возможно, это подходящий инструмент для моей проблемы?
[1] Если я правильно понимаю теорию категорий, такой общий тип технически называется функтор и является хорошо известной концепцией в таких языках, как Haskell.