This:

def __post_init__(self):
    super(NamedObj, self).__post_init__()
    super(NumberedObj, self).__post_init__()
    print("NamedAndNumbered __post_init__")

doesn't do what you think it does. super(cls, obj) will return a proxy to the class after cls in type(obj).__mro__ - so, in your case, to object. And the whole point of cooperative super() calls is to avoid having to explicitely call each of the parents.

The way cooperative super() calls are intended to work is, well, by being "cooperative" - IOW, everyone in the mro is supposed to relay the call to the next class (actually, the super name is a rather sad choice, as it's not about calling "the super class", but about "calling the next class in the mro").

IOW, you want each of your "composable" dataclasses (which are not mixins - mixins only have behaviour) to relay the call, so you can compose them in any order. A first naive implementation would look like:

@dataclass
class NamedObj:
    name: str

    def __post_init__(self):
        super().__post_init__()
        print("NamedObj __post_init__")
        self.name = "Name: " + self.name

@dataclass
class NumberedObj:
    number: int = 0

    def __post_init__(self):
        super().__post_init__()
        print("NumberedObj __post_init__")
        self.number += 1

@dataclass
class NamedAndNumbered(NumberedObj, NamedObj):

    def __post_init__(self):
        super().__post_init__()
        print("NamedAndNumbered __post_init__")

BUT this doesn't work, since for the last class in the mro (here NamedObj), the next class in the mro is the builtin object class, which doesn't have a __post_init__ method. The solution is simple: just add a base class that defines this method as a noop, and make all your composable dataclasses inherit from it:

class Base(object):
    def __post_init__(self):
        # just intercept the __post_init__ calls so they
        # aren't relayed to `object`
        pass

@dataclass
class NamedObj(Base):
    name: str

    def __post_init__(self):
        super().__post_init__()
        print("NamedObj __post_init__")
        self.name = "Name: " + self.name

@dataclass
class NumberedObj(Base):
    number: int = 0

    def __post_init__(self):
        super().__post_init__()
        print("NumberedObj __post_init__")
        self.number += 1

@dataclass
class NamedAndNumbered(NumberedObj, NamedObj):

    def __post_init__(self):
        super().__post_init__()
        print("NamedAndNumbered __post_init__")
        
Answer from bruno desthuilliers on Stack Overflow
Top answer
1 of 3
28

This:

def __post_init__(self):
    super(NamedObj, self).__post_init__()
    super(NumberedObj, self).__post_init__()
    print("NamedAndNumbered __post_init__")

doesn't do what you think it does. super(cls, obj) will return a proxy to the class after cls in type(obj).__mro__ - so, in your case, to object. And the whole point of cooperative super() calls is to avoid having to explicitely call each of the parents.

The way cooperative super() calls are intended to work is, well, by being "cooperative" - IOW, everyone in the mro is supposed to relay the call to the next class (actually, the super name is a rather sad choice, as it's not about calling "the super class", but about "calling the next class in the mro").

IOW, you want each of your "composable" dataclasses (which are not mixins - mixins only have behaviour) to relay the call, so you can compose them in any order. A first naive implementation would look like:

@dataclass
class NamedObj:
    name: str

    def __post_init__(self):
        super().__post_init__()
        print("NamedObj __post_init__")
        self.name = "Name: " + self.name

@dataclass
class NumberedObj:
    number: int = 0

    def __post_init__(self):
        super().__post_init__()
        print("NumberedObj __post_init__")
        self.number += 1

@dataclass
class NamedAndNumbered(NumberedObj, NamedObj):

    def __post_init__(self):
        super().__post_init__()
        print("NamedAndNumbered __post_init__")

BUT this doesn't work, since for the last class in the mro (here NamedObj), the next class in the mro is the builtin object class, which doesn't have a __post_init__ method. The solution is simple: just add a base class that defines this method as a noop, and make all your composable dataclasses inherit from it:

class Base(object):
    def __post_init__(self):
        # just intercept the __post_init__ calls so they
        # aren't relayed to `object`
        pass

@dataclass
class NamedObj(Base):
    name: str

    def __post_init__(self):
        super().__post_init__()
        print("NamedObj __post_init__")
        self.name = "Name: " + self.name

@dataclass
class NumberedObj(Base):
    number: int = 0

    def __post_init__(self):
        super().__post_init__()
        print("NumberedObj __post_init__")
        self.number += 1

@dataclass
class NamedAndNumbered(NumberedObj, NamedObj):

    def __post_init__(self):
        super().__post_init__()
        print("NamedAndNumbered __post_init__")
        
2 of 3
6

The problem (most probably) isn't related to dataclasses. The problem is in Python's method resolution. Calling method on super() invokes the first found method from parent class in the MRO chain. So to make it work you need to call the methods of parent classes manually:

@dataclass
class NamedAndNumbered(NumberedObj, NamedObj):

    def __post_init__(self):
        NamedObj.__post_init__(self)
        NumberedObj.__post_init__(self)
        print("NamedAndNumbered __post_init__")

Another approach (if you really like super()) could be to continue the MRO chain by calling super() in all parent classes (but it needs to have a __post_init__ in the chain):

@dataclass
class MixinObj:
    def __post_init__(self):
        pass

@dataclass
class NamedObj(MixinObj):
    name: str

    def __post_init__(self):
        super().__post_init__()
        print("NamedObj __post_init__")
        self.name = "Name: " + self.name

@dataclass
class NumberedObj(MixinObj):
    number: int = 0

    def __post_init__(self):
        super().__post_init__()
        print("NumberedObj __post_init__")
        self.number += 1

@dataclass
class NamedAndNumbered(NumberedObj, NamedObj):

    def __post_init__(self):
        super().__post_init__()
        print("NamedAndNumbered __post_init__")

In both approaches:

>>> nandn = NamedAndNumbered('n_and_n')
NamedObj __post_init__
NumberedObj __post_init__
NamedAndNumbered __post_init__
>>> print(nandn.name)
Name: n_and_n
>>> print(nandn.number)
1
🌐
Stack Overflow
stackoverflow.com › questions › 79296865 › how-to-make-a-python-dataclass-mixin
How to make a Python Dataclass mixin? - Stack Overflow
As of python 3.11, you can declare a class decorated with @dataclass_transform, and subclasses will then be treated similarly to a dataclass by type checkers/language servers, in that they will typehint calls to the class’s constructor with any class attributes/defaults declared in the class.
🌐
GitHub
gist.github.com › mahyarmirrashed › 39615ef0d9f6f915ccab267ec3ef8f9c
Python Dataclass Dictionary Mixin · GitHub
Python Dataclass Dictionary Mixin. GitHub Gist: instantly share code, notes, and snippets.
🌐
Ritviknag
dcw.ritviknag.com › en › latest › common_use_cases › wizard_mixins.html
Wizard Mixin Classes — Dataclass Wizard 0.39.1 documentation
The TOML Wizard provides an easy, convenient interface for converting dataclass instances to/from TOML. This mixin enables simple loading, saving, and flexible serialization of TOML data, including support for custom key casing transforms. ... By default, NO key transform is used in the TOML ...
🌐
Python
docs.python.org › 3 › library › dataclasses.html
dataclasses — Data Classes
1 month ago - Source code: Lib/dataclasses.py This module provides a decorator and functions for automatically adding generated special methods such as__init__() and__repr__() to user-defined classes. It was ori...
🌐
SQLAlchemy
docs.sqlalchemy.org › en › 21 › orm › dataclasses.html
Integration with dataclasses and attrs — SQLAlchemy 2.1 Documentation
SQLAlchemy as of version 2.0 features “native dataclass” integration where an Annotated Declarative Table mapping may be turned into a Python dataclass by adding a single mixin or decorator to mapped classes.
🌐
Real Python
realpython.com › python-mixin
What Are Mixin Classes in Python? – Real Python
August 6, 2025 - As a result, calling mixin_instance.as_json() produces an empty JSON object ({}) encoded as a Python string. Note: Mixins must either remain stateless or manage their state carefully to reduce the risk of interference with the data attributes of the classes they’re mixed into. So, you’ll rarely find a custom constructor—the .__init__() method—in a mixin class. To truly benefit from your mixin, you should integrate it into another class with its own attributes, such as this one: ... >>> from dataclasses import dataclass >>> @dataclass ...
Find elsewhere
🌐
PyPI
pypi.org › project › dataclass-sqlalchemy-mixins
dataclass-sqlalchemy-mixins
JavaScript is disabled in your browser · Please enable JavaScript to proceed · A required part of this site couldn’t load. This may be due to a browser extension, network issues, or browser settings. Please check your connection, disable any ad blockers, or try using a different browser
🌐
Smarie
smarie.github.io › python-mixture
mixture
July 15, 2025 - You can obviously use more advanced libraries such as attrs or dataclasses but be aware that by default they create an __init__ method for you. The intent here is to provide a "minimal viable product" to define class-level fields without creating __init__ methods. In python we can already use such mixin classes without additional framework, simply by inheriting from them thanks to python's multiple inheritance capabilities:
Top answer
1 of 2
4

The correct solution is to abandon the DataclassMixin classes and simply make the abstract classes into dataclasses, like this:

@dataclass  # type: ignore[misc]
class A(ABC):
    a_field: int = 1

    @abstractmethod
    def method(self):
        pass

@dataclass  # type: ignore[misc]
class B(A):
    b_field: int = 2

@dataclass
class C(B):
    c_field: int = 3

    def method(self):
        return self

The reason for the failures is that, as explained in the documentation on dataclasses, the complete set of fields in a dataclass is determined when the dataclass is compiled, not when it is inherited from. The internal code that generates the dataclass's __init__ function can only examine the MRO of the dataclass as it is declared on its own, not when mixed in to another class.

It's necessary to add # type: ignore[misc] to each abstract dataclass's @dataclass line, not because the solution is wrong but because mypy is wrong. It is mypy, not Python, that requires dataclasses to be concrete. As explained by ilevkivskyi in mypy issue 5374, the problem is that mypy wants a dataclass to be a Type object and for every Type object to be capable of being instantiated. This is a known problem and awaits a resolution.

The behavior in the question and in the solution is exactly how dataclasses should behave. And, happily, abstract dataclasses that inherit this way (the ordinary way) can be mixed into other classes willy-nilly no differently than other mix-ins.

2 of 2
1

Putting the mixin as the last base class works without error:

@dataclass
class ADataclassMixin:
    a_field: int = 1


class A(ABC, ADataclassMixin):

    @abstractmethod
    def method(self):
        pass


@dataclass
class BDataclassMixin:  
    b_field: int = 2


class B(A, BDataclassMixin):

    def method(self):
        return self


o = B(a_field=5)
print((o.a_field, o.b_field))  # (5,2)
🌐
PyPI
pypi.org › project › dataclass-wizard
dataclass-wizard · PyPI
Dataclass Wizard allows you to extend serialization and deserialization to support custom or unsupported types via type hooks. ... This makes it easy to support standard-library types such as ipaddress.IPv4Address as well as user-defined classes.
      » pip install dataclass-wizard
    
Published   Jan 06, 2026
Version   0.39.1
🌐
Medium
medium.com › @akashsdas_dev › dataclasses-in-python-804db8e149c3
Dataclasses in Python. Reduce writing boilerplate code when… | by AkashSDas | Medium
March 23, 2024 - Dataclasses in Python Reduce writing boilerplate code when working with classes using the dataclasses provided by Python. Usually when we want to define an entity in our system using classes, we …
🌐
SQLAlchemy
docs.sqlalchemy.org › en › 20 › orm › dataclasses.html
Integration with dataclasses and attrs — SQLAlchemy 2.0 Documentation
March 5, 2023 - SQLAlchemy as of version 2.0 features “native dataclass” integration where an Annotated Declarative Table mapping may be turned into a Python dataclass by adding a single mixin or decorator to mapped classes.
🌐
Ritviknag
dcw.ritviknag.com › en › latest
Why Dataclass Wizard? — Dataclass Wizard 0.39.1 documentation
Dataclass Wizard allows you to extend serialization and deserialization to support custom or unsupported types via type hooks. ... This makes it easy to support standard-library types such as ipaddress.IPv4Address as well as user-defined classes. Type hooks work with or without inheritance, ...
🌐
Ivergara
ivergara.github.io › ABC-and-dataclasses.html
Having fun with dataclasses and abstract base classes - On data, programming, and technology
January 28, 2019 - When I saw the inclusion of the dataclass module in the standard library of Python 3.7, I told myself I wanted to use it. Being able to reduce even more the boilerplate in Python seemed like a great idea. Of course I could have already been using attrs for basically the same effect, but when I tried it it didn’t feel natural. That was most likely due to a lack of experience on my part. Thus, it’s very obvious that I’d end up mixing abstract classes, abstract collections, and dataclasses eventually at some point.
🌐
GitHub
github.com › lidatong › dataclasses-json
GitHub - lidatong/dataclasses-json: Easily serialize Data Classes to and from JSON
Using the dataclass_json decorator or mixing in DataClassJsonMixin will provide you with an additional method .schema().
Starred by 1.5K users
Forked by 163 users
Languages   Python 100.0% | Python 100.0%
🌐
SQLAlchemy
docs.sqlalchemy.org › en › 14 › orm › dataclasses.html
Integration with dataclasses and attrs — SQLAlchemy 1.4 Documentation
March 8, 2024 - SQLAlchemy 2.0 will include a new dataclass integration feature which allows for a particular class to be mapped and converted into a Python dataclass simultaneously, with full support for SQLAlchemy’s declarative syntax.
🌐
GitHub
github.com › sqlalchemy › sqlalchemy › discussions › 10105
How to create a mixin for a declarative dataclass model, when the mixin adds a column with a default value? · sqlalchemy/sqlalchemy · Discussion #10105
July 14, 2023 - I'm trying to wrap my head around how the declarative class mapping works. What I'm trying to achieve is add a deleted_at field to a class via a mixin so that it: defaults to None can be de...
Author   sqlalchemy
🌐
The New Stack
thenewstack.io › home › python dataclasses: a complete guide to boilerplate‑free objects
Python Dataclasses: A Complete Guide to Boilerplate‑Free Objects - The New Stack
October 9, 2025 - You can match objects based on their field (released in 3.10) because dataclasses integrate seamlessly with structural pattern matching. Inheritance and mixins both allow classes to reuse or extend behavior and functionality from other classes, making code more modular and easier to maintain.