I was wondering if putting tighter constraints on the type hints in child classes is bad practice or not. Consider the following example
from typing import List, Tuple
from abc import abstractmethod
class Foo:
@abstractmethod
def print_stuff(self, items: List[str]):
"""print some list."""
class Bar(Foo):
def print_stuff(self, items: Tuple[str, str]):
print(f"Items: {items}, always 2.")
class Baz(Foo):
def print_stuff(self, items: Tuple[str, str, str]):
print(f"Items: {items}, always 3.")
Here the base class expects some list of strings. In Bar the method only allows exactly two strings, and in Baz exactly three. Is this increased restriction bad practice?
Thank you!
>Solution :
You can use the ellipsis dots to indicate the number of arguments in the tuple is variable. You should avoid using List in your parent class and Tuple in your child class as they are fundamentally different types.
from typing import List, Tuple
from abc import abstractmethod
class Foo:
@abstractmethod
def print_stuff(self, items: Tuple[str, ...]):
"""print some list."""
class Bar(Foo):
def print_stuff(self, items: Tuple[str, str]):
print(f"Items: {items}, always 2.")
class Baz(Foo):
def print_stuff(self, items: Tuple[str, str, str]):
print(f"Items: {items}, always 3.")
This is still not great practice as the Bar and Baz return different lengths of tuples which violates Liskov substitution; mypy will probably throw an error.
The real question is: does it have to be a tuple, or is any sequence acceptable? Then you should use:
from typing import Sequence
from abc import abstractmethod
class Foo:
@abstractmethod
def print_stuff(self, items: Sequence[str]):
"""print some list."""
class Bar(Foo):
def print_stuff(self, items: Sequence[str]):
print(f"Items: {items}, always 2.")
class Baz(Foo):
def print_stuff(self, items: Sequence[str]):
print(f"Items: {items}, always 3.")