이펙티브 파이썬에서 메타클래스를 사용을 하여 기존의 클래스들을 리팩토링하는 방법들을 제시해주었다.
훌륭한 제안들을 아무 생각없이 읽다가 문득, 응? 근데 메타클래스가 뭐임?
한마디로 처음부터 다시 읽어야 했다.
메타클래스란 클래스 생성하는 것을 제어하는 클래스라고 볼 수 있다.
나는 이 정의를 보고 클래스를 vaildation 해주는 클래스라고 이해했다. 클래스의 클래스 랄까?
정의를 보았으니 메타클래스가 만들어진 흐름까지 보고 싶었다.
- 파이썬 초기버전: 파이썬의 초기 버전에서는 메타클래스와 같은 개념이 명확하게 존재하지 않음.
대신 클래스와 타입은 별개의 것으로 취급.
이 때문에 일반 사용자 클래스와 내장 타입 (예: 리스트, 딕셔너리) 사이에 뚜렷한 차이존재. - 파이썬 2.x 시리즈: 파이썬 2.x에서는 메타클래스 개념이 소개.
이는 클래스의 동작을 수정하거나 확장하는 데 사용.
그럼에도 불구하고, 클래스와 타입 사이에는 여전히 명확한 구분존재. - 파이썬 2.2: 이 버전에서 중요한 변화.
type과 class의 구분이 사라지면서 모든 클래스가 type의 인스턴스로 간주되기 시작.
이는 "모든 것은 객체다"는 파이썬의 철학을 좀 더 명확하게 반영한 것. - 파이썬 3.x 시리즈: 파이썬 3에서는 메타클래스의 사용이 좀 더 간편.
metaclass 키워드를 통해 클래스 정의 시에 메타클래스를 지정가능.
결론적으로는 메타클래스는 클래스의 행동 자체를 정의하기 위해 탄생했다고 볼 수 있다.
class Meta(type):
def __new__(cls, name, bases, attrs):
# 클래스 이름이 항상 대문자로 시작하도록 강제
if not name[0].isupper():
raise ValueError("Class names should start with an uppercase letter.")
return super().__new__(cls, name, bases, attrs)
class MyClass(metaclass=Meta):
pass
위는 메타클래스를 사용한 코드다.
위와 같이 클래스에 메타클래스를 파라미터로 전달함으로써 클래스의 행동을 강제할 수 있다.
하지만 메타클래스를 사용하게 되었을 때 아래코드와 같이 합성성을 해치는 경우가 생긴다.
class Meta1(type):
def __new__(cls, name, bases, class_dict):
class_dict['meta1_function'] = lambda self: "Meta1 function"
return super().__new__(cls, name, bases, class_dict)
class Meta2(type):
def __new__(cls, name, bases, class_dict):
class_dict['meta2_function'] = lambda self: "Meta2 function"
return super().__new__(cls, name, bases, class_dict)
class ClassWithMeta1(metaclass=Meta1):
pass
class ClassWithMeta2(metaclass=Meta2):
pass
# 두 메타클래스의 기능을 동시에 사용하려면?
# 파이썬은 클래스에 하나의 메타클래스만 할당할 수 있기 때문에 이는 불가능합니다.
# class CombinedClass(metaclass=Meta1, metaclass=Meta2):
# pass # 이런 코드는 작동하지 않습니다.
이를 __ init_subclass __ 를 통해 개선해볼 수 있는데,
init_subclass는 네임 그대로 subclass(자식)을 초기화하는 역활이라고 이해하면 좋을 것 같다.
class BaseClass:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
if hasattr(cls, "meta1"):
cls.meta1_function = lambda self: "Meta1 function"
if hasattr(cls, "meta2"):
cls.meta2_function = lambda self: "Meta2 function"
class ClassWithMeta1(BaseClass):
meta1 = True
class ClassWithMeta2(BaseClass):
meta2 = True
class CombinedClass(BaseClass):
meta1 = True
meta2 = True
a = ClassWithMeta1()
b = ClassWithMeta2()
c = CombinedClass()
print(a.meta1_function()) # 출력: Meta1 function
# print(b.meta1_function()) # AttributeError
print(b.meta2_function()) # 출력: Meta2 function
print(c.meta1_function()) # 출력: Meta1 function
print(c.meta2_function()) # 출력: Meta2 function
'프로그래밍 언어 > Python' 카테고리의 다른 글
GIL를 무조건 신뢰하면 안되는 이유( feat.스레드 감수성 ) (0) | 2023.09.15 |
---|---|
두 코드의 차이점을 설명하시요( feat. Python,쓰레드, GIL, 병렬 I/O) (0) | 2023.09.15 |
클래스를 디버깅해보자(feat.메타클래스,클래스 데코레이터) (0) | 2023.09.15 |
디스크립터 __set_name__ 의 활용방법 (0) | 2023.09.14 |
@classmethod 는 왜 쓸까? (0) | 2023.09.12 |