Python:Mixin
Python 에서 Mixin 구현 방법.
Mixin 클래스의 자동 초기화 방법
다음은 정상적으로 작동하지 않는 코드이다:
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__
는 호출되지만, _Test1.__init__
은 호출되지 않는다. Python 에서는 다중 상속 시 명시되지 않는 한 첫 번째 상속 클래스의 초기화만 사용한다.
따라서 Mixin 클래스를 사용하고 싶을 경우, 다음과 같이 __new__
함수를 구현하면 된다:
# -*- 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")
참고로 출력은 다음과 같다:
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
보면 알겠지만 다이아몬드 상속 구조 이다.
이 경우에 최 상단에 있는 _TestBase
클래스의 __new__
함수가 한 번만 호출된 것을 알 수 있다.