Search code examples
pythoninheritancepython-dataclasses

Dataclass inheriting using kw_only for all variables


I am practicing on using the super function and dataclass inheritance in general. I have enabled the kw_only attribute for cases when the parent class has default values. I completely understand that super doesn't need to be used in a dataclass if you're just passing variables and I can avoid using super here. My goal is to understand the super feature better through this example. I can't understand the error message I'm getting though.

@dataclass(kw_only=True)
class ZooAnimals():

    food_daily_kg: int
    price_food: float 
    area_required: float
    name: str 
   
    
c = ZooAnimals(food_daily_kg=565, price_food=40, area_required=10, name='Monkey'
              )

print(c)


@dataclass(kw_only=True)
class Cats(ZooAnimals):
    meowing: str
    def __init__(self, food_daily_kg, price_food, area_required, meowing, name):
        self.meowing = meowing
        super().__init__(food_daily_kg, price_food, area_required, name)
        
            
z = Cats(food_daily_kg=465, price_food=30, area_required=10, meowing='Little Bit', 
         name='Leopard'
        )
print(z)

Output:

ZooAnimals(food_daily_kg=565, price_food=40, area_required=10, name='Monkey')

TypeError: ZooAnimals.__init__() takes 1 positional argument but 5 were given

Solution

  • You shouldn't define an __init__ method in a data class if you don't have any custom initialization logics. And if you do have custom initialization logics, you should define them in a __post_init__ method instead.

    Your code produces the error because the __init__ method of the subclass calls the __init__ method of the base class with positional arguments when the method is configured to accept keyword arguments only with your kw_only=True option.

    You can fix it by passing keyword arguments instead, or by simply removing the __init__ method from the subclass entirely since one would be generated for the data class with inheritance in mind already.

    For example, this subclass definition would work just fine:

    @dataclass(kw_only=True)
    class Cats(ZooAnimals):
        meowing: str
    

    Demo here

    Or if you would still like to define a custom __init__ method for some reason:

    @dataclass(kw_only=True)
    class Cats(ZooAnimals):
        meowing: str
        def __init__(self, food_daily_kg, price_food, area_required, meowing, name):
            self.meowing = meowing
            super().__init__(
                food_daily_kg=food_daily_kg,
                price_food=price_food,
                area_required=area_required,
                name=name)
    

    Demo here