1

I constructed multiple base dataclass. Now I want to construct a child dataclass inheriting from these base classes, but may inherit part of attributes defined in some base classes. Examples can be:

import dataclasses

@dataclasses.dataclass
class A:
    a: int = None
    b: float = None
    c: str = None

@dataclasses.dataclass
class B:
    d: int = None
    b: float = 3.5

@dataclasses.dataclass
class C:
    e: int = None
    f: float = 3.5
    g: int = None

@dataclasses.dataclass
class D(A, B):
    def __post_init__(self):
        for _field, _field_property in C.__dataclass_fields__.items():
            if _field != "g":
                setattr(self, _field, _field_property.default)

Namely, I want to construct a child class D inheriting A and B, and attributes in C except g. Checking the child class D

>>> D.__dataclass_fields__.keys() # got dict_keys(['d', 'b', 'a', 'c'])
>>> d = D(a=4, b=2, c=5, d=3.4, e=2.1, g=55)
Traceback (most recent call last):
  File "<pyshell#77>", line 1, in <module>
    d = D(a=4, b=2, c=5, d=3.4, e=2.1, g=55)
TypeError: __init__() got an unexpected keyword argument 'e'

And

>>> D.__dict__.keys()
dict_keys(['__module__', '__post_init__', '__doc__', '__dataclass_params__', '__dataclass_fields__', '__init__', '__repr__', '__eq__', '__hash__'])

When I changed __post_init__ to __init__ and using super().__init__() for the inheritance, still can't the attributes from class C and lose the advantage of dataclass, i.e.,

>>> @dataclasses.dataclass
class D(A, B):
    def __init__(self):
        super().__init__()
        for _field, _field_property in C.__dataclass_fields__.items():
            if _field != "g":
                setattr(self, _field, _field_property.default)

And run

>>> d = D(a=4, b=2, c=5, d=3.4, e=2.1, g=55)
Traceback (most recent call last):
  File "<pyshell#81>", line 1, in <module>
    d = D(a=4, b=2, c=5, d=3.4, e=2.1, g=55)
TypeError: __init__() got an unexpected keyword argument 'a'

What should I do?

3
  • I've bumped into this too and I don't think dataclasses really supports inheritance. Commented Mar 9, 2020 at 7:14
  • 1
    Create class C' that is like C without the g attribute, C becomes a subclass of C' adding the new g attribute, and your D inherits A,B, C'. Dataclasses work at class creation time, __post_init__ works at instance creation time, i.e. way too late to modify the definition of __init__ Commented Mar 9, 2020 at 7:23
  • It's good that you found a working solution, but you should also consider using composition over inheritance. In the case you're presenting, it looks like class D might be better off having instances of A, B, and maybe C_part instead of inheriting them. Commented Mar 9, 2020 at 13:00

1 Answer 1

1

Similar as what proposed by @GiacomoAlzetta, I suddenly come out that idea using dataclasses.make_dataclass, i.e., generate a copy of C but excluding attribute g, i.e.,

<<< C_part = dataclasses.make_dataclass("C_part", [(_field, _field_property.type, _field_property.default) for _field, _field_property in C.__dataclass_fields__.items() if _field != "g"])

Thus I have

>>> C_part.__dataclass_fields__.keys()  # dict_keys(['e', 'f'])

Then the D can be obtained by

>>> @dataclasses.dataclass
class D(A, B, C_part):
    pass

>>> d = D(a=4, b=2, c=5, d=3.4, e=2.1, f=55)
>>> d
D(e=2.1, f=55, d=3.4, b=2, a=4, c=5)
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.