Skip to content
Search

Python:Mixin

lastmod 2022-08-04created 2022-08-042 edits

How to implement a Mixin in Python.

Automatic initialization of Mixin classes

The following code does not work correctly:

class _Test0:

    value0: list

    def __init__(self):
        print("_Test0.__init__")
        self.value0 = list()


class _Test1:

    value1: dict

    def __init__(self):
        print("_Test1.__init__")
        self.value1 = dict()


class _Test2(_Test0, _Test1):
    pass


a = _Test2()
print(a.value0)
print(a.value1)  # AttributeError

_Test0.__init__ is called, but _Test1.__init__ is not. With multiple inheritance, Python uses only the initializer of the first base class unless told otherwise.

So if you want to use Mixin classes, implement the __new__ function as follows:

# -*- coding: utf-8 -*-

numbering = 0


class _TestBase:
    __uid: int
    _module: int

    def __new__(cls, *args, **kwargs):
        instance = super().__new__(cls)
        global numbering
        instance.uid = numbering
        numbering += 1
        print(f"_TestBase.__new__(uid={instance.uid})")
        return instance


class _Test0(_TestBase):

    value0: list

    def __new__(cls, *args, **kwargs):
        print("_Test0.__new__")
        instance = super().__new__(cls)
        instance.value0 = list()
        return instance


class _Test1(_TestBase):

    value1: dict

    def __new__(cls, *args, **kwargs):
        print("_Test1.__new__")
        instance = super().__new__(cls)
        instance.value1 = dict()
        return instance


class _Test2(_Test0, _Test1):
    def __new__(cls, *args, **kwargs):
        print("_Test2.__new__")
        return super().__new__(cls, *args, **kwargs)

    def __init__(self):
        pass


print("new a")
a = _Test2()
print(a.value0)
print(a.value1)

print("new b")
b = _Test2()
print("-------")
print(b.value0)
print(b.value1)
# print(b.__uid)
print("=======")

a.value0.append(0)
a.value1.setdefault("a", 100)
b.value0.append(1)
b.value1.setdefault("b", 200)
print(a.value0)
print(a.value1)
print("-------")
print(b.value0)
print(b.value1)

print("=======")
try:
    print(_Test2.value0)
except AttributeError:
    print("_Test2.value0 AttributeError")

try:
    print(_Test2.value1)
except AttributeError:
    print("_Test2.value0 AttributeError")

For reference, the output is:

new a
_Test2.__new__
_Test0.__new__
_Test1.__new__
_TestBase.__new__(uid=0)
[]
{}
new b
_Test2.__new__
_Test0.__new__
_Test1.__new__
_TestBase.__new__(uid=1)
-------
[]
{}
=======
[0]
{'a': 100}
-------
[1]
{'b': 200}
=======
_Test2.value0 AttributeError
_Test2.value0 AttributeError

As you can see, this is a diamond inheritance structure.

    _TestBase
     /     \
 _Test0   _Test1
     \     /
     _Test2

In this case, you can see that the __new__ function of the topmost _TestBase class is called only once.

See also

author Zer0 sitemap