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.
In this case, you can see that the __new__ function of the topmost _TestBase class is called only once.