Данный проект разработан для реализации обобщенного программирования, аналогичного принятому в C++. Он призван решить следующие проблемы:
- Потерю сведений о шаблоне при создании экземпляра, проявляющуюся в:
- Потере
typing.get_origin. Так, например, кодожидаемо возвращаетfrom typing import get_origin get_origin(list[int])
list, тогда как при создании экземпляравозвращаемое значение меняется наfrom typing import get_origin get_origin(list[int](i for i in range(5)))
None. - Потере
typing.get_args. Так, например, кодожидаемо возвращаетfrom typing import get_args get_args(list[int])
(int,), тогда как при создании экземпляравозвращаемое значение меняется на пустой кортеж.from typing import get_args get_args(list[int](i for i in range(5)))
- Отсутствие стандартной возможности запросить переданные шаблону
параметры из экземпляра. Стандартная реализация
typingзадает аттрибут__orig_class__, но он не регламентирован и не доступен в некоторых ситуациях (например, в конструкторе). - Неразличимость экземпляров различных шаблонов, в частности
type(list[str]()) == type(list[int]())возвращаетTrue.
Основным предлагаемым решением является динамическое создание дочерних классов,
обладающих аттрибутом __args__, хранящим словарь, отображающий TypeVar в
переданные шаблону параметры. Для того чтобы обеспечить возвращение одних и тех
же классов при одинаковых параметрах шаблона используется словарь
EvGen.__cache__, отображающий пары (шаблон, параметры) в конкретные классы.
Для начала определим два класса, на примере которых будут продемонстрированы основные возможности такого подхода:
from typing import Self
from CppLikePyTemplate import EvTemplate
class TemplateClassA[TP1, TP2](EvTemplate):
@classmethod
def printResolved(cls: type[Self]) -> None:
print(f"{__class__.__qualname__}.TP1 = {cls._EvResolve(TP1)}")
print(f"{__class__.__qualname__}.TP2 = {cls._EvResolve(TP2)}")
class TemplateClassB[TP1, TP2, TP3](TemplateClassA[TP1 | TP2, str]):
@classmethod
def printResolved(cls: type[Self]) -> None:
print(f"{__class__.__qualname__}.TP1 = {cls._EvResolve(TP1)}")
print(f"{__class__.__qualname__}.TP2 = {cls._EvResolve(TP2)}")
print(f"{__class__.__qualname__}.TP3 = {cls._EvResolve(TP3)}")
super().printResolved()- Попытка использования шаблона с неверным количеством параметров
приводит к соответствующей ошибке:
TemplateClassA[int]
ValueError: Number of parameters does not match template - При передаче шаблону корректного набора параметров ошибка не
возникает и становится возможным запросить переданные параметры,
например, через описанный выше метод
printResolved:Что приводит к выводу программы:TemplateClassA[int, float].printResolved()
TemplateClassA.TP1 = <class 'int'> TemplateClassA.TP2 = <class 'float'> - Данные шаблоны можно использовать в качестве родительских классов,
причем разрешение параметров происходит на всех уровнях наследования.
Так, использование описанного выше шаблона
TemplateClassBприводит к выводу:TemplateClassB[int, float, str].printResolved()
TemplateClassB.TP1 = <class 'int'> TemplateClassB.TP2 = <class 'float'> TemplateClassB.TP3 = <class 'str'> TemplateClassA.TP1 = int | float TemplateClassA.TP2 = <class 'str'> - Задание одинаковых значений приводит не к созданию новой сущности, а к
возврату уже созданной, то есть
TemplateClassA[int, float] == TemplateClassB[int, float]возвращаетTrue, аTemplateClassA[int, float] == TemplateClassB[int, str]вернетFalse.
- Реализовать поддержку основных типов параметров шаблонов:
- реализовать поддержку
TypeVar(2026.04.21); - реализовать поддержку
TypeVarTuple; - реализовать поддержку
ParamSpec:- реализовать поддержку
ParamSpecArgs; - реализовать поддержку
ParamSpecKwargs.
- реализовать поддержку
- реализовать поддержку
- Реализовать разрешение «составных типов» (объединений, опциональных, т.д.),
определяемых
typing(2026.04.22). - Реализовать разрешение
EvGenбез наследования. - Реализовать кеширование шаблонов таким образом, чтобы одинаковые параметры приводили к использованию одного и того же экземпляра (2026.04.22).