The way dataclasses combines attributes prevents you from being able to use attributes with defaults in a base class and then use attributes without a default (positional attributes) in a subclass.

That's because the attributes are combined by starting from the bottom of the MRO, and building up an ordered list of the attributes in first-seen order; overrides are kept in their original location. So Parent starts out with ['name', 'age', 'ugly'], where ugly has a default, and then Child adds ['school'] to the end of that list (with ugly already in the list). This means you end up with ['name', 'age', 'ugly', 'school'] and because school doesn't have a default, this results in an invalid argument listing for __init__.

This is documented in PEP-557 Dataclasses, under inheritance:

When the Data Class is being created by the @dataclass decorator, it looks through all of the class's base classes in reverse MRO (that is, starting at object) and, for each Data Class that it finds, adds the fields from that base class to an ordered mapping of fields. After all of the base class fields are added, it adds its own fields to the ordered mapping. All of the generated methods will use this combined, calculated ordered mapping of fields. Because the fields are in insertion order, derived classes override base classes.

and under Specification:

TypeError will be raised if a field without a default value follows a field with a default value. This is true either when this occurs in a single class, or as a result of class inheritance.

You do have a few options here to avoid this issue.

The first option is to use separate base classes to force fields with defaults into a later position in the MRO order. At all cost, avoid setting fields directly on classes that are to be used as base classes, such as Parent.

The following class hierarchy works:

# base classes with fields; fields without defaults separate from fields with.
@dataclass
class _ParentBase:
    name: str
    age: int
    
@dataclass
class _ParentDefaultsBase:
    ugly: bool = False

@dataclass
class _ChildBase(_ParentBase):
    school: str

@dataclass
class _ChildDefaultsBase(_ParentDefaultsBase):
    ugly: bool = True

# public classes, deriving from base-with, base-without field classes
# subclasses of public classes should put the public base class up front.

@dataclass
class Parent(_ParentDefaultsBase, _ParentBase):
    def print_name(self):
        print(self.name)

    def print_age(self):
        print(self.age)

    def print_id(self):
        print(f"The Name is {self.name} and {self.name} is {self.age} year old")

@dataclass
class Child(_ChildDefaultsBase, Parent, _ChildBase):
    pass

By pulling out fields into separate base classes with fields without defaults and fields with defaults, and a carefully selected inheritance order, you can produce an MRO that puts all fields without defaults before those with defaults. The reversed MRO (ignoring object) for Child is:

_ParentBase
_ChildBase
_ParentDefaultsBase
Parent
_ChildDefaultsBase

Note that while Parent doesn't set any new fields, it does inherit the fields from _ParentDefaultsBase and should not end up 'last' in the field listing order; the above order puts _ChildDefaultsBase last so its fields 'win'. The dataclass rules are also satisfied; the classes with fields without defaults (_ParentBase and _ChildBase) precede the classes with fields with defaults (_ParentDefaultsBase and _ChildDefaultsBase).

The result is Parent and Child classes with a sane field older, while Child is still a subclass of Parent:

>>> from inspect import signature
>>> signature(Parent)
<Signature (name: str, age: int, ugly: bool = False) -> None>
>>> signature(Child)
<Signature (name: str, age: int, school: str, ugly: bool = True) -> None>
>>> issubclass(Child, Parent)
True

and so you can create instances of both classes:

>>> jack = Parent('jack snr', 32, ugly=True)
>>> jack_son = Child('jack jnr', 12, school='havard', ugly=True)
>>> jack
Parent(name='jack snr', age=32, ugly=True)
>>> jack_son
Child(name='jack jnr', age=12, school='havard', ugly=True)

Another option is to only use fields with defaults; you can still make in an error to not supply a school value, by raising one in __post_init__:

_no_default = object()

@dataclass
class Child(Parent):
    school: str = _no_default
    ugly: bool = True

    def __post_init__(self):
        if self.school is _no_default:
            raise TypeError("__init__ missing 1 required argument: 'school'")

but this does alter the field order; school ends up after ugly:

<Signature (name: str, age: int, ugly: bool = True, school: str = <object object at 0x1101d1210>) -> None>

and a type hint checker will complain about _no_default not being a string.

You can also use the attrs project, which was the project that inspired dataclasses. It uses a different inheritance merging strategy; it pulls overridden fields in a subclass to the end of the fields list, so ['name', 'age', 'ugly'] in the Parent class becomes ['name', 'age', 'school', 'ugly'] in the Child class; by overriding the field with a default, attrs allows the override without needing to do a MRO dance.

attrs supports defining fields without type hints, but lets stick to the supported type hinting mode by setting auto_attribs=True:

import attr

@attr.s(auto_attribs=True)
class Parent:
    name: str
    age: int
    ugly: bool = False

    def print_name(self):
        print(self.name)

    def print_age(self):
        print(self.age)

    def print_id(self):
        print(f"The Name is {self.name} and {self.name} is {self.age} year old")

@attr.s(auto_attribs=True)
class Child(Parent):
    school: str
    ugly: bool = True
Answer from Martijn Pieters on Stack Overflow
Top answer
1 of 16
397

The way dataclasses combines attributes prevents you from being able to use attributes with defaults in a base class and then use attributes without a default (positional attributes) in a subclass.

That's because the attributes are combined by starting from the bottom of the MRO, and building up an ordered list of the attributes in first-seen order; overrides are kept in their original location. So Parent starts out with ['name', 'age', 'ugly'], where ugly has a default, and then Child adds ['school'] to the end of that list (with ugly already in the list). This means you end up with ['name', 'age', 'ugly', 'school'] and because school doesn't have a default, this results in an invalid argument listing for __init__.

This is documented in PEP-557 Dataclasses, under inheritance:

When the Data Class is being created by the @dataclass decorator, it looks through all of the class's base classes in reverse MRO (that is, starting at object) and, for each Data Class that it finds, adds the fields from that base class to an ordered mapping of fields. After all of the base class fields are added, it adds its own fields to the ordered mapping. All of the generated methods will use this combined, calculated ordered mapping of fields. Because the fields are in insertion order, derived classes override base classes.

and under Specification:

TypeError will be raised if a field without a default value follows a field with a default value. This is true either when this occurs in a single class, or as a result of class inheritance.

You do have a few options here to avoid this issue.

The first option is to use separate base classes to force fields with defaults into a later position in the MRO order. At all cost, avoid setting fields directly on classes that are to be used as base classes, such as Parent.

The following class hierarchy works:

# base classes with fields; fields without defaults separate from fields with.
@dataclass
class _ParentBase:
    name: str
    age: int
    
@dataclass
class _ParentDefaultsBase:
    ugly: bool = False

@dataclass
class _ChildBase(_ParentBase):
    school: str

@dataclass
class _ChildDefaultsBase(_ParentDefaultsBase):
    ugly: bool = True

# public classes, deriving from base-with, base-without field classes
# subclasses of public classes should put the public base class up front.

@dataclass
class Parent(_ParentDefaultsBase, _ParentBase):
    def print_name(self):
        print(self.name)

    def print_age(self):
        print(self.age)

    def print_id(self):
        print(f"The Name is {self.name} and {self.name} is {self.age} year old")

@dataclass
class Child(_ChildDefaultsBase, Parent, _ChildBase):
    pass

By pulling out fields into separate base classes with fields without defaults and fields with defaults, and a carefully selected inheritance order, you can produce an MRO that puts all fields without defaults before those with defaults. The reversed MRO (ignoring object) for Child is:

_ParentBase
_ChildBase
_ParentDefaultsBase
Parent
_ChildDefaultsBase

Note that while Parent doesn't set any new fields, it does inherit the fields from _ParentDefaultsBase and should not end up 'last' in the field listing order; the above order puts _ChildDefaultsBase last so its fields 'win'. The dataclass rules are also satisfied; the classes with fields without defaults (_ParentBase and _ChildBase) precede the classes with fields with defaults (_ParentDefaultsBase and _ChildDefaultsBase).

The result is Parent and Child classes with a sane field older, while Child is still a subclass of Parent:

>>> from inspect import signature
>>> signature(Parent)
<Signature (name: str, age: int, ugly: bool = False) -> None>
>>> signature(Child)
<Signature (name: str, age: int, school: str, ugly: bool = True) -> None>
>>> issubclass(Child, Parent)
True

and so you can create instances of both classes:

>>> jack = Parent('jack snr', 32, ugly=True)
>>> jack_son = Child('jack jnr', 12, school='havard', ugly=True)
>>> jack
Parent(name='jack snr', age=32, ugly=True)
>>> jack_son
Child(name='jack jnr', age=12, school='havard', ugly=True)

Another option is to only use fields with defaults; you can still make in an error to not supply a school value, by raising one in __post_init__:

_no_default = object()

@dataclass
class Child(Parent):
    school: str = _no_default
    ugly: bool = True

    def __post_init__(self):
        if self.school is _no_default:
            raise TypeError("__init__ missing 1 required argument: 'school'")

but this does alter the field order; school ends up after ugly:

<Signature (name: str, age: int, ugly: bool = True, school: str = <object object at 0x1101d1210>) -> None>

and a type hint checker will complain about _no_default not being a string.

You can also use the attrs project, which was the project that inspired dataclasses. It uses a different inheritance merging strategy; it pulls overridden fields in a subclass to the end of the fields list, so ['name', 'age', 'ugly'] in the Parent class becomes ['name', 'age', 'school', 'ugly'] in the Child class; by overriding the field with a default, attrs allows the override without needing to do a MRO dance.

attrs supports defining fields without type hints, but lets stick to the supported type hinting mode by setting auto_attribs=True:

import attr

@attr.s(auto_attribs=True)
class Parent:
    name: str
    age: int
    ugly: bool = False

    def print_name(self):
        print(self.name)

    def print_age(self):
        print(self.age)

    def print_id(self):
        print(f"The Name is {self.name} and {self.name} is {self.age} year old")

@attr.s(auto_attribs=True)
class Child(Parent):
    school: str
    ugly: bool = True
2 of 16
191

Note that with Python 3.10, it is now possible to do it natively with dataclasses.

Dataclasses 3.10 added the kw_only attribute (similar to attrs). It allows you to specify which fields are keyword_only, thus will be set at the end of the init, not causing an inheritance problem.

Taking directly from Eric Smith's blog post on the subject:

There are two reasons people [were asking for] this feature:

  • When a dataclass has many fields, specifying them by position can become unreadable. It also requires that for backward compatibility, all new fields are added to the end of the dataclass. This isn't always desirable.
  • When a dataclass inherits from another dataclass, and the base class has fields with default values, then all of the fields in the derived class must also have defaults.

What follows is the simplest way to do it with this new argument, but there are multiple ways you can use it to use inheritance with default values in the parent class:

from dataclasses import dataclass

@dataclass(kw_only=True)
class Parent:
    name: str
    age: int
    ugly: bool = False

@dataclass(kw_only=True)
class Child(Parent):
    school: str

ch = Child(name="Kevin", age=17, school="42")
print(ch.ugly)

Take a look at the blogpost linked above for a more thorough explanation of kw_only.

Cheers !

PS: As it is fairly new, note that your IDE might still raise a possible error, but it works at runtime

🌐
Medium
medium.com › swlh › python-dataclass-inheritance-finally-686eaf60fbb5
Python dataclass inheritance, finally ! | by Anis Campos
October 25, 2021 - But truth be told, losing the required aspect of fields was infuriating, but still acceptable when considering the huge work provided by the dataclass, i.e, all the boilerplate removed (immutability, constructor, comparator, hash, to string, etc…) Now not only can we freely inherit and never find ourselves adding default values to untold amount of fields (when you have several layer of inheritance, try adding a default value in the base class and see for yourself…) but we also can forget about the need to order the fields in the class depending on whether they are optional or required.
Discussions

Dataclasses with Inheritance?
Imagine the init() that you'd make if this weren't a dataclass. You'd put parameters in a certain order, and you couldn't do the thing that the error message is complaining about. More on reddit.com
🌐 r/learnpython
11
3
April 18, 2023
Dataclasses and non-dataclasses inheritance
My codebase included (over many modules) code like: class C0: pass @dataclass class DC2: my_field: bool = True class C1(C0, DC2): # original state pass And lo it was good: print(len(C1.__dataclass_fields__)) # 1, life is good But then I tried turning the non-dataclass parent into a dataclass: ... More on discuss.python.org
🌐 discuss.python.org
0
April 19, 2025
Dataclasses: subclassing a dataclass without its fields inherited as init-fields
I was wondering if it would be possible to allow subclassing a dataclass without automatically including its fields in Subclass.__init__ (in some sense, hiding the inherited fields). When subclassing the dataclass AB below to create CD, the fields of AB become fields of CD, automatically included ... More on discuss.python.org
🌐 discuss.python.org
0
August 12, 2024
Class inheritance in Python 3.7 dataclasses
I think it's worth noting that within the attrs / dataclass typed python paradigm, composition is usually preferred over inheritance. Extending your subclass's __init__ like this is vaguely a violation of LSP, because your various subclasses won't be interchangeable. More on stackoverflow.com
🌐 stackoverflow.com
🌐
Python
docs.python.org › 3 › library › dataclasses.html
dataclasses — Data Classes
3 weeks ago - @dataclass class C: a: int # 'a' has no default value b: int = 0 # assign a default value for 'b' In this example, both a and b will be included in the added __init__() method, which will be defined as: ... TypeError will be raised if a field without a default value follows a field with a default value. This is true whether this occurs in a single class, or as a result of class inheritance.
🌐
Reddit
reddit.com › r/learnpython › dataclasses with inheritance?
r/learnpython on Reddit: Dataclasses with Inheritance?
April 18, 2023 -

I have a class Animal and Dog which inherits Animal. Why is it that I get an error if I try to give my Dog class a breed field?

TypeError: non-default argument 'breed' follows default argument

This is my code

from dataclasses import dataclass


@dataclass
class Animal:
    species: str
    arms: int
    legs: int


@dataclass
class Dog(Animal):
    breed: str
    species: str = "Dog"
    arms: int = 0
    legs: int = 4


if __name__ == '__main__':
    jake = Dog(breed="Bulldog")
    print(jake)

I did find that if I add a breed field to Animal I wouldn't get the error.

🌐
Python.org
discuss.python.org › python help
Dataclasses and non-dataclasses inheritance - Python Help - Discussions on Python.org
April 19, 2025 - My codebase included (over many modules) code like: class C0: pass @dataclass class DC2: my_field: bool = True class C1(C0, DC2): # original state pass And lo it was good: print(len(C1.__dataclass_fields__)) # 1, life is good But then I tried turning the non-dataclass parent into a dataclass: @dataclass class DC1: pass class C2(DC1, DC2): # messes up __dataclass_fields__ pass print(len(C2.__dataclass_fields__)) # 0 ?? Making the child a dataclass changes the s...
Find elsewhere
🌐
Python.org
discuss.python.org › python help
Dataclasses: subclassing a dataclass without its fields inherited as init-fields - Python Help - Discussions on Python.org
August 12, 2024 - I was wondering if it would be possible to allow subclassing a dataclass without automatically including its fields in Subclass.__init__ (in some sense, hiding the inherited fields). When subclassing the dataclass AB below to create CD, the fields of AB become fields of CD, automatically included ...
🌐
GeeksforGeeks
geeksforgeeks.org › python › data-classes-in-python-set-4-inheritance
Data Classes in Python | Set 4 (Inheritance) - GeeksforGeeks
July 11, 2025 - Both SuperClass and SubClass are DataClasses - although super-class or sub-class being a normal class is also possible. When a DataClass inherits a normal class, the __init__() from the super-class is overridden in sub-class.
🌐
Python Morsels
pythonmorsels.com › customizing-dataclass-initialization
Customizing dataclass initialization - Python Morsels
October 18, 2024 - All classes inherit from the built-in object class, and the __setattr__ method on object does the actual attribute-setting behind the scenes. So we can make new instances of this dataclass without an exception being raised: ... And we still ...
Top answer
1 of 16
397

The way dataclasses combines attributes prevents you from being able to use attributes with defaults in a base class and then use attributes without a default (positional attributes) in a subclass.

That's because the attributes are combined by starting from the bottom of the MRO, and building up an ordered list of the attributes in first-seen order; overrides are kept in their original location. So Parent starts out with ['name', 'age', 'ugly'], where ugly has a default, and then Child adds ['school'] to the end of that list (with ugly already in the list). This means you end up with ['name', 'age', 'ugly', 'school'] and because school doesn't have a default, this results in an invalid argument listing for __init__.

This is documented in PEP-557 Dataclasses, under inheritance:

When the Data Class is being created by the @dataclass decorator, it looks through all of the class's base classes in reverse MRO (that is, starting at object) and, for each Data Class that it finds, adds the fields from that base class to an ordered mapping of fields. After all of the base class fields are added, it adds its own fields to the ordered mapping. All of the generated methods will use this combined, calculated ordered mapping of fields. Because the fields are in insertion order, derived classes override base classes.

and under Specification:

TypeError will be raised if a field without a default value follows a field with a default value. This is true either when this occurs in a single class, or as a result of class inheritance.

You do have a few options here to avoid this issue.

The first option is to use separate base classes to force fields with defaults into a later position in the MRO order. At all cost, avoid setting fields directly on classes that are to be used as base classes, such as Parent.

The following class hierarchy works:

# base classes with fields; fields without defaults separate from fields with.
@dataclass
class _ParentBase:
    name: str
    age: int
    
@dataclass
class _ParentDefaultsBase:
    ugly: bool = False

@dataclass
class _ChildBase(_ParentBase):
    school: str

@dataclass
class _ChildDefaultsBase(_ParentDefaultsBase):
    ugly: bool = True

# public classes, deriving from base-with, base-without field classes
# subclasses of public classes should put the public base class up front.

@dataclass
class Parent(_ParentDefaultsBase, _ParentBase):
    def print_name(self):
        print(self.name)

    def print_age(self):
        print(self.age)

    def print_id(self):
        print(f"The Name is {self.name} and {self.name} is {self.age} year old")

@dataclass
class Child(_ChildDefaultsBase, Parent, _ChildBase):
    pass

By pulling out fields into separate base classes with fields without defaults and fields with defaults, and a carefully selected inheritance order, you can produce an MRO that puts all fields without defaults before those with defaults. The reversed MRO (ignoring object) for Child is:

_ParentBase
_ChildBase
_ParentDefaultsBase
Parent
_ChildDefaultsBase

Note that while Parent doesn't set any new fields, it does inherit the fields from _ParentDefaultsBase and should not end up 'last' in the field listing order; the above order puts _ChildDefaultsBase last so its fields 'win'. The dataclass rules are also satisfied; the classes with fields without defaults (_ParentBase and _ChildBase) precede the classes with fields with defaults (_ParentDefaultsBase and _ChildDefaultsBase).

The result is Parent and Child classes with a sane field older, while Child is still a subclass of Parent:

>>> from inspect import signature
>>> signature(Parent)
<Signature (name: str, age: int, ugly: bool = False) -> None>
>>> signature(Child)
<Signature (name: str, age: int, school: str, ugly: bool = True) -> None>
>>> issubclass(Child, Parent)
True

and so you can create instances of both classes:

>>> jack = Parent('jack snr', 32, ugly=True)
>>> jack_son = Child('jack jnr', 12, school='havard', ugly=True)
>>> jack
Parent(name='jack snr', age=32, ugly=True)
>>> jack_son
Child(name='jack jnr', age=12, school='havard', ugly=True)

Another option is to only use fields with defaults; you can still make in an error to not supply a school value, by raising one in __post_init__:

_no_default = object()

@dataclass
class Child(Parent):
    school: str = _no_default
    ugly: bool = True

    def __post_init__(self):
        if self.school is _no_default:
            raise TypeError("__init__ missing 1 required argument: 'school'")

but this does alter the field order; school ends up after ugly:

<Signature (name: str, age: int, ugly: bool = True, school: str = <object object at 0x1101d1210>) -> None>

and a type hint checker will complain about _no_default not being a string.

You can also use the attrs project, which was the project that inspired dataclasses. It uses a different inheritance merging strategy; it pulls overridden fields in a subclass to the end of the fields list, so ['name', 'age', 'ugly'] in the Parent class becomes ['name', 'age', 'school', 'ugly'] in the Child class; by overriding the field with a default, attrs allows the override without needing to do a MRO dance.

attrs supports defining fields without type hints, but lets stick to the supported type hinting mode by setting auto_attribs=True:

import attr

@attr.s(auto_attribs=True)
class Parent:
    name: str
    age: int
    ugly: bool = False

    def print_name(self):
        print(self.name)

    def print_age(self):
        print(self.age)

    def print_id(self):
        print(f"The Name is {self.name} and {self.name} is {self.age} year old")

@attr.s(auto_attribs=True)
class Child(Parent):
    school: str
    ugly: bool = True
2 of 16
191

Note that with Python 3.10, it is now possible to do it natively with dataclasses.

Dataclasses 3.10 added the kw_only attribute (similar to attrs). It allows you to specify which fields are keyword_only, thus will be set at the end of the init, not causing an inheritance problem.

Taking directly from Eric Smith's blog post on the subject:

There are two reasons people [were asking for] this feature:

  • When a dataclass has many fields, specifying them by position can become unreadable. It also requires that for backward compatibility, all new fields are added to the end of the dataclass. This isn't always desirable.
  • When a dataclass inherits from another dataclass, and the base class has fields with default values, then all of the fields in the derived class must also have defaults.

What follows is the simplest way to do it with this new argument, but there are multiple ways you can use it to use inheritance with default values in the parent class:

from dataclasses import dataclass

@dataclass(kw_only=True)
class Parent:
    name: str
    age: int
    ugly: bool = False

@dataclass(kw_only=True)
class Child(Parent):
    school: str

ch = Child(name="Kevin", age=17, school="42")
print(ch.ugly)

Take a look at the blogpost linked above for a more thorough explanation of kw_only.

Cheers !

PS: As it is fairly new, note that your IDE might still raise a possible error, but it works at runtime

🌐
DataCamp
datacamp.com › tutorial › python-data-classes
Python Data Classes: A Comprehensive Tutorial | DataCamp
March 15, 2024 - Learn the fundamentals of object-oriented programming in Python, including classes, objects, inheritance, encapsulation, and polymorphism.
🌐
Mattdood
mattdood.com › 2023 › 1 › python-dataclass-inheritance-with-default-values-is-wonky-20230124114200
python dataclass inheritance with default values is wonky | Matthew Wimberly
January 24, 2023 - This is particularly true with inheritance, where I noticed that I received a "recursive limit reached" when attempting to return an asdict() representation. To fix this, I used a workaround that returns the dictionary representation using fields to create a key-value pairing. from dataclasses import dataclass, field, fields from typing import Union # Example set of keys to remove before returning to the consumer REMOVE_THESE_KEYS = {'_internal_key_id_one', '_internal_key_id_two'} @dataclass class Example: pass class ExampleChild(Example): pass def get_dict_representation(datarepresentation: Union[Example, ExampleChild]) -> Dict: return dict( (field.name, getattr(datarepresentation, field.name)) for field in fields(datarepresentation) if field.name not in REMOVE_THESE_KEYS )
🌐
Python
bugs.python.org › issue36077
Issue 36077: Inheritance dataclasses fields and default init statement - Python tracker
This issue tracker has been migrated to GitHub, and is currently read-only. For more information, see the GitHub FAQs in the Python's Developer Guide · This issue has been migrated to GitHub: https://github.com/python/cpython/issues/80258
🌐
Medium
medium.vaningelgem.be › inheritance-in-python-dataclasses-9b882fd5b68c
Inheritance in Python dataclasses | by Steven Van Ingelgem | Medium
November 21, 2022 - @dataclass class Child(Parent): name: str child_attr: str @property def parent_name(self): return super().name print(child.parent_name)
🌐
Real Python
realpython.com › python-data-classes
Data Classes in Python (Guide) – Real Python
March 8, 2024 - Inheritance rules require that any non-default field in a subclass cannot follow defaulted base-class fields. When working through this tutorial, you’ll also get to compare dataclasses with Python’s namedtuple and attrs and identify when each option fits best.
Top answer
1 of 2
2

Once I got around to finding out how to do this with Pydantic, I found it is exceedingly easy, at least as long as you are dealing with such a small difference between the two structures. You'll need to install pydantic to try the following working example:

from pydantic import BaseModel, Field


class Animal(BaseModel):
    genus: str = Field(alias="breed")
    color: str
    name: str

    class Config:
        allow_population_by_field_name = True


spot = Animal(genus="retriever", color="brown", name="spot")

json_v1 = spot.json(by_alias=True)
json_v2 = spot.json()

print("v1 out:", json_v1)
print("v2 out:", json_v2)

animal_v1 = Animal.parse_raw(json_v1)
animal_v2 = Animal.parse_raw(json_v2)

print("v1 in:", animal_v1)
print("v2 in:", animal_v2)

Output:

v1 out: {"breed": "retriever", "color": "brown", "name": "spot"}
v2 out: {"genus": "retriever", "color": "brown", "name": "spot"}
v1 in: genus='retriever' color='brown' name='spot'
v2 in: genus='retriever' color='brown' name='spot'

If you aren't familiar with Pydantic, it builds upon dataclasses to add a lot of really useful features. Every time I dig into it, I find more interesting things. I even used it recently to help me generate an antiquated wire format that you can't get libraries for, at least not for free. I've wrestled with serialization for decades and it's a really good tool. Other really good frameworks like FastAPI are built upon it as well.

2 of 2
1

2 is by far the simplest. However, that just perpetuates the problem.

How is this used? If it's possible to get to a point where you can use this without knowing which version it is then that's work worth doing. Any work you do along the lines of 1 should be aimed at that goal. What you build shouldn't just emerge from looking at the data. Consider what's going to be done with this.

You said the responses aren't going to change. Which makes me think these are immutable. So we don't have to worry about saving updates. In that case I'd lean towards your own data class that can only be populated with what you need. Write methods to populate it from either version. Now you can call things what you want to call them.

🌐
Python
peps.python.org › pep-0557
PEP 557 – Data Classes | peps.python.org
@dataclass class C: a: int # 'a' has no default value b: int = 0 # assign a default value for 'b' In this example, both a and b will be included in the added __init__ method, which will be defined as: ... TypeError will be raised if a field without a default value follows a field with a default value. This is true either when this occurs in a single class, or as a result of class inheritance.
🌐
Python.org
discuss.python.org › ideas
Default __post_init__ Implementation in Dataclasses - Ideas - Discussions on Python.org
July 4, 2024 - When using inheritance with dataclasses, if a parent class does not define a __post_init__ method, calling super().__post_init__() in the child class will raise an AttributeError, which is in contrast to super().__init__() Here’s a minimal example to illustrate the issue: from dataclasses ...
🌐
GitHub
github.com › python › cpython › issues › 139497
Improve documentation on `dataclass` inheritance · Issue #139497 · python/cpython
October 2, 2025 - You obviously expect a subclass to inherit all methods and parameters of all its parent classes, so that not applying to dataclass fields unless you use a decorator is perhaps unexpected. Especially as is_dataclass(D) == True.
Author   MarkRotchell
🌐
Studytonight
studytonight.com › post › inheritance-in-python-dataclass
Inheritance in Python Dataclass - Studytonight
June 24, 2023 - By redefining attributes or methods ... the rest of the parent dataclass. Absolutely! Python allows you to create multi-level inheritance hierarchies with dataclasses....