How to create a collection class that is able to manage and create objects of a certain class and its subclasses with proper type hints

Viewed 27

I have a class Thing that represents some thing and a class ThingCollection that manages a collection of such things. Like this:

class Thing:
    def __init__(self, a: int) -> None:
        self.a = a


class ThingCollection:
    def __init__(self) -> None:
        self.things = []

    # ...

    def load(self, things_to_load) -> None:
        # Simplified, would maybe load from a file
        self.things = [Thing(**x) for x in things_to_load]

This can be used like this:

coll = ThingCollection()
coll.load([{"a": 12}])

Now, I'd like to have subclasses of Things and manage those with the same ThingCollection. Like this:

class ExtendedThing(Thing):
    def __init__(self, a: int, b: int) -> None:
        super().__init__(a)
        self.b = b


class ThingCollection2:
    def __init__(self) -> None:
        self.things = []

    # ...

    def load(self, things_to_load, which_class) -> None:
        self.things = [which_class(**x) for x in things_to_load]

This works fine:

coll2 = ThingCollection2()
coll2.load([{"a": 12, "b": 13}], ExtendedThing)
print(coll2.things[0].b)

However, if I want to add type hints, the type checker complains. I.e., changing the signature of load to:

    def load(self, things_to_load, which_class: Type[Thing]) -> None:

will lead to a complaint of Cannot access member "b" for type "Thing" Member "b" is unknown for the print statement above.

My questions:

  1. What are alternative approaches that resolve this problem? One would be to also subclass the ThingCollection to, e.g., ExtendedThingCollection, but that feels clumsy and redundant.
  2. What are pros and cons of the different approaches other than related to the type hint problem above?
  3. What would be the right terminology to ask the question I'm asking? Are there design patterns that are relevant with regard to this question?
0 Answers
Related