I have a class hierarchy. I'd like to be able to:
- avoid repeating base class constructor parameters in child classes
- create instances in Python code with intellisense hints for both child and parent constructor arguments
- create instances from a given dict (loaded from yaml config)
To satisfy 1
, I can use **kwargs
in child constructors but then I lose intellisense 2
.
3
requires explicit type conversion on certain fields (str
--> Dog.Breed
, str
--> int
, etc.) in Python code so I can't just pass yaml dict as **kwargs
. Additionally I have to essentially repeat parameters listed in __init__
method in each child class.
How can I achieve all 3 of the above code qualities Python?
from abc import ABC, abstractmethod
from enum import Enum
from typing import Dict
import pydoc
class Animal(ABC):
def __init__(self, name: str, age: int):
self.name = name
self.age = age
@abstractmethod
def say(self) -> str:
pass
class Dog(Animal):
class Breed(Enum):
Bulldog = 1
Puddle = 2
def __init__(
self, breed: Breed, **kwargs
): # Because of **kwargs, Intellisense won't know base class arguments
super().__init__(**kwargs)
self.breed = breed
def say(self):
return "woof"
@classmethod
def from_config(cls, config: Dict) -> "Dog":
return cls(
breed=cls.Breed[config["breed"]],
name=config["name"], # name and age should somehow be in the base class
age=int(config["age"]),
)
class Cat(Animal):
def __init__(self, tail_length: float, **kwargs):
super().__init__(**kwargs)
self.tail_length = tail_length
def say(self):
return "meow"
@classmethod
def from_config(cls, config: Dict) -> "Cat":
return cls(
tail_length=config["tail_length"],
name=config["name"], # this code is duplicate from Dog class
age=int(config["age"]),
)
def test_from_config():
config = {
"name": "Charlie",
"breed": "Bulldog",
"age": 2,
}
dog = Dog.from_config(config)
assert dog.name == "Charlie"
assert dog.breed == Dog.Breed.Bulldog
def test_intellisense():
assert "breed" in pydoc.render_doc(Dog)
assert "breed" not in pydoc.render_doc(Cat)
assert "tail_length" not in pydoc.render_doc(Dog)
assert "tail_length" in pydoc.render_doc(Cat)
assert "age" in pydoc.render_doc(Animal)
assert "age" in pydoc.render_doc(Dog)
assert "age" in pydoc.render_doc(Cat)