I am trying to create types with certain constraints.
Because I want these constraints to be arbitrarily complex, I've decided that I do not need them to be type-checked, but I do want them to travel with the type definitions.
As an example, say I want to create a homogeneous container (i.e. typing.List
) constrained to a size. For this particular example, I know I should use Tuple[int, int]
(as per this question) but that's not flexible enough for other use cases.
My desired functionality:
from typing import List
class MyList(List):
def __init__(self, *, num_elements: int) -> None:
self.num_elements = num_elements
def validate(self, input: List) -> None:
if len(to_validate) > self.num_elements:
raise ValueError
class MyClass:
myvar: MyList(num_elements=2)[int] # should look like List[int] to a type checker
def __init__(self, *, myvar: MyList(num_elements=2)[int]): # I guess I need to define twice?
self.myvar = myvar
self.validate_all()
def validate_all(self):
for var in self.__annotations__:
if hasattr(self.__annotations__[var], "validate"):
self.__annotations__[var].validate(getattr(self, var))
MyClass(myvar=(1, 2)) # pass
MyClass(myvar=(1, 2, 3)) # fail
As noted above, the annotation for myvar
would have to look like List[int]
to a type checker like mypy
so that at least that part can be handled by existing frameworks.
I know I probably need to do something with typing.Generic
or typing.TypeVar
but I tried and honestly don't understand how they would be applicable to this situation.