Skip to content

Python:Metaclass

파이썬에선 클래스도 객체이다. 클래스도 객체라면 클래스를 만드는 또 다른 클래스가 있단 말인가? 있다!! 이를 "메타 클래스"라 부른다. 클래스로 객체를 만들 듯 메타 클래스로 클래스를 만들 수 있다는 의미이다.

설명

class MyMetaclass(type):
    def __new__(cls, name, bases, namespace):
        # Magic happens here
        return super().__new__(cls, name, bases, namespace)

class MyClass(metaclass=MyMetaclass):
    pass

obj = MyClass()

파이썬의 클래스는 단순히 객체를 위한 설계도가 아닙니다. 클래스 자체도 객체입니다! 모든 객체에는 자신을 생성한 클래스가 필요합니다. 그렇다면 클래스 객체를 생성하는 것은 무엇일까요? 바로 메타클래스입니다 .

기본적으로 Python은 모든 클래스를 생성할 때 type 메타클래스를 사용합니다. 예를 들어, 다음 두 클래스는 서로 동일합니다.

# Create a MyClass object
class MyClass:
    ...
obj = MyClass()

# Also creates a MyClass object
obj2 = type("MyClass", (), {})

To break down what those arguments mean, here is an example that creates a class with an attribute x and a method say_hi, that also subclasses off object.

# type(
#     name,
#     bases,
#     attributes
# )
CustomClass = type(
    'CustomClass',
    (object,),
    {'x': 5, 'say_hi': lambda self: 'Hello!'}
)

obj = CustomClass()
print(obj.x)  # 5
print(obj.say_hi())  # Hello!

본질적으로 메타클래스를 사용하면 클래스 생성 중에 이러한 인수를 사용자 지정하고 수정할 수 있습니다. 예를 들어, 클래스의 모든 정수 속성을 두 배로 늘리는 메타클래스는 다음과 같습니다.

class DoubleAttrMeta(type):
    def __new__(cls, name, bases, namespace):
        new_namespace = {}
        for key, val in namespace.items():
            if isinstance(val, int):
                val *= 2
            new_namespace[key] = val
        return super().__new__(cls, name, bases, new_namespace)

class MyClass(metaclass=DoubleAttrMeta):
    x = 5
    y = 10

print(MyClass.x)  # 10
print(MyClass.y)  # 20

레지스트리에 생성된 모든 클래스를 등록하는 메타클래스의 또 다른 예는 다음과 같습니다.

# ===== Metaclass Solution =====
class RegisterMeta(type):
    registry = []
    def __new__(mcs, name, bases, attrs):
        cls = super().__new__(mcs, name, bases, attrs)
        mcs.registry.append(cls)
        return cls

문제는, decorators 가 흑마법을 사용하지 않고도 이와 같은 목표를 달성할 수 있다는 것입니다 (그리고 흑마법이 종종 더 깔끔하기도 합니다).

# ===== Decorator Solution =====
def register(cls):
    registry.append(cls)
    return cls

@register
class MyClass:
    pass

그리고 그것은 메타클래스의 가장 큰 문제점을 드러냅니다.

거의 100%의 경우, 당신은 결코 그것들을 만질 필요가 없을 것입니다.

일상적인 개발 과정에서 코드의 99%는 메타클래스가 유용한 사용 사례에 도달하지 못할 것입니다. 그리고 그 1% 중 95%는 일반 데코레이터, 던더 메서드, 또는 단순 상속만으로도 해결할 수 있습니다.

그래서 유명한 파이썬 명언이 하나 있습니다.

메타클래스는 사용자의 99%가 걱정할 필요도 없는 심오한 마법입니다. 메타클래스가 필요한지 고민한다면, 필요 없습니다. - 팀 피터스

하지만 만약 여러분이 메타클래스만이 해결할 수 있는 고유한 문제를 가진 1%에 속한다면, 메타클래스는 Python 객체 시스템의 내부를 조작할 수 있게 해주는 강력한 도구입니다.

메타클래스의 실제 사례는 다음과 같습니다:

  • Python의 "ABC" 구현은 메타클래스를 사용하여 추상 클래스를 구현합니다.
  • Python의 "Enum" 구현은 이를 사용하여 열거형 유형을 생성합니다.
  • Django, SQLAlchemy, Pydantic, Pytest와 같은 많은 타사 라이브러리는 다양한 목적으로 메타클래스를 사용합니다.

See also

Favorite site

References


  1. 14_Advanced_Python_Features_-_Edward_Li's_Blog.pdf