Dataclasses and non-dataclasses inheritance

dataclasses uses a decorator, so the generation of new methods is not inherited and needs to be applied on each class you wish to have the dataclass methods. This is the only thing that is specific to dataclasses.

The observed behaviour then follows from standard inheritance as it does for hand written classes.

class Base1:
    def __init__(self, arg1="arg1"):
        self.arg1 = arg1
    def __repr__(self):
        return f"{self.__class__.__name__}(arg1={self.arg1!r})"

class Base2:
    def __init__(self):
        pass
    def __repr__(self):
        return f"{self.__class__.__name__}()"

class Child1(Base1, Base2):
    pass

class Child2(Base2, Base1):
    pass

print(Child1())  # Child1(arg1='arg1')
print(Child2())  # Child2()

This is not to say that it’s not easy to forget to place the @dataclass decorator, just that if it is missing then this behaviour is what I would expect.

If you want the dataclass features applied automatically to inheriting classes to avoid this possibility, then you need to make the base class perform the application. Note that you may want to force kw_only=True as the order of arguments will depend on the inheritance order.

from dataclasses import dataclass, field
from typing import dataclass_transform

@dataclass_transform(field_specifiers=(field,))
class DCBase:
    def __init_subclass__(cls, /, **kwargs):
        # optional: check for slots=True in kwargs and error
        dataclass(cls, **kwargs)

class DC1(DCBase):
    arg1: str = "arg1"

class DC2(DCBase):
    arg2: str = "arg2"

class IC1(DC1, DC2):
    pass

class IC2(DC2, DC1):
    pass

print(IC1())  # IC1(arg2='arg2', arg1='arg1')
print(IC2())  # IC2(arg1='arg1', arg2='arg2')