Example Code
I = TypeVar("I", bound=Optional[Iterable])
O = TypeVar("O", bound=Optional[Mappable])
class Worker(Generic[I, O]):
@abstractmethod
def do_work(self, input: I) -> O:
pass
worker = Worker[list, dict]()
worker_with_optional = Worker[Optional[list], Optional[dict]]()
worker_bad_types = Worker[Optional[list], dict]()
The actual code in the example may seem a bit contrived, but it was the best way I could think to abstractly represent my problem. What I’m struggling to do is set up TypeVars
for the input to Generic
that would allow users to create worker
and worker_with_optional
but not worker_bad_types
.
The concept that I’m struggling to put into code is the relatedness of the two input types. I want to require that they are I
and O
are both Optional, or they are both required to have a value.
The best workaround for the functionality I want would be to make two versions of the class like this:
I = TypeVar("I", bound=Iterable)
O = TypeVar("O", bound=Mappable)
class Worker(Generic[I, O]):
@abstractmethod
def do_work(self, input: I) -> O:
pass
class WorkerWithOptional(Generic[I, O]):
@abstractmethod
def do_work(self, input: Optional[I]) -> Optional[O]:
pass
worker = Worker[list, dict]()
worker_with_optional = WorkerWithOptional[list, dict]()
# worker_bad_types now has a type error because None is not Iterable
worker_bad_types = Worker[Optional[int], str]()
This approach accomplishes the type restrictions I want, but I’d rather not make two copies of my class to account for this. Is there a way to relate the Optionality of the types passed into Generic
so that they always match?