__mro_entries__
메서드를 통해서 mro 상속 체인을 만듭니다.super()
를 사용할 때 접근하는 클래스의 순서와 같습니다.TypedDict
이 있습니다. 일반 dict
처럼 선언해서 사용가능하고, 특이하게 부모 클래스로 사용해서 선언할 수 있습니다. 런타임엔 일반 dict
입니다.# 클래스처럼 선언, 이 클래스는 dict가 아님.
class NMT(TypedTuple):
a: int
b: int
type(NMT(a=1, b=1)) # 사용한 후에는 dict
해당 클래스는 특이하게도 사실 함수고, __mro_entries__
메서드를 활용해서 상속 체인을 구현하고 있습니다.
def TypedDict(typename, fields=None, /, *, total=True, **kwargs):
ns = {'__annotations__': dict(fields)}
try:
ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__')
except (AttributeError, ValueError):
pass
return _TypedDictMeta(typename, (), ns, total=total)
_TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {})
TypedDict.__mro_entries__ = lambda bases: (_TypedDict,)
__qualname__
을 기본 컨텍스트로 사용할 수 있습니다.__prepare__
훅에서 이 시점에 사용할 네임스페이스를 정의할 수 있습니다. 해당 메서드는 반드시 클래스메서드이어야 하며(메타 클래스의 인스턴스가 생성되기 전이므로 인스턴스가 존재하지 않습니다) 정의되지 않았다면 빈 OrderdDict
입니다.metaclass.__new__
)__new__
다음 __init__
이전) 에 디스크립터의 __set_name__
메서드가 실행됩니다.type.__new__
)의 마지막 과정입니다.__init_subclass__
가 호출됩니다.class.__init_sublcass__
훅은 개인적으로는 별개의 메타클래스를 선언할 필요가 없기 때문에 특별한 이유가 없다면 가장 선호하는 방법입니다.__init__
훅을 통해 해당 인스턴스가 초기화됩니다. (metaclass.__init__
)class meta(type):
def __new__(cls, *args):
print('new1', cls)
ret = super().__new__(cls, *args)
print('new2', cls)
return ret
def __init__(self, *args, **kwargs):
print('init1', self)
super().__init__(*args, **kwargs)
print('init2', self)
@classmethod
def __prepare__(cls, *args):
print('prepare', cls)
return {}
class A(metaclass=meta):
def __init_subclass__(cls) -> None:
print('init_sub', cls)
class B(A):
pass
# 결과
prepare <class '__main__.meta'> A 준비
new1 <class '__main__.meta'> A 생성
new2 <class '__main__.meta'> A 생성
init1 <class '__main__.A'> A 초기화
init2 <class '__main__.A'> A 초기화
prepare <class '__main__.meta'> B 준비
new1 <class '__main__.meta'> B 생성
init_sub <class '__main__.B'> B 생성 마지막에 서브클래스 훅
new2 <class '__main__.meta'> B 생성
init1 <class '__main__.B'> B 초기화
init2 <class '__main__.B'> B 초기화
https://docs.python.org/ko/3/reference/datamodel.html#metaclasses