I'm having an abstract class in Python like this
class Sinker(ABC):
@abstractmethod
def batch_write(self, data) -> None:
pass
The concrete class is expected to write data to some cloud databases. My intention of writing abstract class this way was to make sure the concrete classes always implement the batch_write()
method.
However, I'm not sure what type hints I should put in for data
because one of my concrete class is expecting List[str]
while another concrete class is expecting List[dict]
.
Here are just some options popped up in my head so far.
Any
List[Any]
List[str|dict]
What would be a better way to do so? Is there a style guide for this situation?
I tried out all three options and since they're just type hints, it won't cause me any trouble. But I just want to make sure I'm writing codes that are aligned with corresponding best practices in OOP if there's any.
You can make Sinker
a generic class so the type of items in the list passed to batch_write
can be parameterized:
from abc import ABC, abstractmethod
class Sinker[T](ABC):
@abstractmethod
def batch_write(self, data: list[T]) -> None:
...
class StrSinker(Sinker[str]):
def batch_write(self, data: list[str]) -> None:
pass
class DictSinker(Sinker[dict]):
def batch_write(self, data: list[dict]) -> None:
pass
Demo with Pyright
This way, the type checker would be able to spot an incorrect type hint for you. For example, with:
class StrSinker(Sinker[str]):
def batch_write(self, data: list[int]) -> None:
pass
Pyright would produce the following complaint:
Method "batch_write" overrides class "Sinker" in an incompatible manner
Parameter 2 type mismatch: base parameter is type "list[str]", override parameter is type "list[int]"
"list[str]" is incompatible with "list[int]"
Type parameter "_T@list" is invariant, but "str" is not the same as "int"
Consider switching from "list" to "Sequence" which is covariant (reportIncompatibleMethodOverride)
Demo with Pyright