pythonoop

Should I (and how should I) align the types hints my abstract class and concrete class in Python?


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.

  1. Any
  2. List[Any]
  3. 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.


Solution

  • 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