본문 바로가기
C.W.K.
Stream
Lesson 04 of 05 · published

메타클래스 — 클래스를 만드는 과정에 끼어들기

~22 min · metaclass, type, class-creation, advanced

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

클래스도 객체고 기본 메타클래스는 type이야

type(name, bases, namespace)로 class 문과 같은 객체를 만들 수 있어. 메타클래스는 클래스 객체가 만들어질 때 이름·부모·본문을 검사하거나 바꿔.

강력한 만큼 적용 범위가 넓어

등록, 선언 검증, 프레임워크 규약처럼 모든 하위 클래스 생성에 개입해야 할 때 쓸 수 있지만, 상속 계층 전체의 동작을 어렵게 만들고 다른 메타클래스와 충돌할 수 있어.

먼저 더 작은 도구를 골라

하위 클래스가 생길 때 알림만 필요하면 __init_subclass__, 한 클래스만 바꾸면 클래스 데코레이터가 대개 충분해. 메타클래스는 두 방법으로 표현되지 않는 클래스 생성 규칙에 남겨.

Code

type으로 클래스를 직접 만들기·python
# 둘은 같음

# 형태 1
class Foo:
    x = 10
    def hello(self):
        return "hi"

# 형태 2
Foo2 = type("Foo2", (), {"x": 10, "hello": lambda self: "hi"})

f1 = Foo()
f2 = Foo2()
print(f1.x, f1.hello())
print(f2.x, f2.hello())

print(type(Foo))         # <class 'type'>
print(type(Foo2))        # <class 'type'>
작은 사용자 정의 메타클래스·python
class UpperAttrMeta(type):
    def __new__(mcs, name, bases, namespace):
        upper_namespace = {}
        for key, value in namespace.items():
            if not key.startswith("__"):
                upper_namespace[key.upper()] = value
            else:
                upper_namespace[key] = value
        return super().__new__(mcs, name, bases, upper_namespace)

class Foo(metaclass=UpperAttrMeta):
    x = 10
    def hello(self):
        return "hi"

print(Foo.X)             # 10  — 자동 rename
print(Foo().HELLO())     # 'hi'  — 메서드도 rename
__init_subclass__로 더 단순하게 풀기·python
class Plugin:
    registry = {}

    def __init_subclass__(cls, name=None, **kwargs):
        super().__init_subclass__(**kwargs)
        Plugin.registry[name or cls.__name__] = cls

class JsonPlugin(Plugin, name="json"):
    def process(self, data):
        import json
        return json.dumps(data)

class YamlPlugin(Plugin, name="yaml"):
    def process(self, data):
        return f"yaml: {data}"

print(Plugin.registry)
# {'json': <class '...JsonPlugin'>, 'yaml': <class '...YamlPlugin'>}

# 서브클래스가 자동 등록 — metaclass 불필요
클래스 데코레이터로 한 클래스만 바꾸기·python
def auto_repr(cls):
    def __repr__(self):
        attrs = ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
        return f"{cls.__name__}({attrs})"
    cls.__repr__ = __repr__
    return cls

@auto_repr
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

print(Point(3, 4))         # Point(x=3, y=4)

# 옛날엔 metaclass 연습이었어. 이제 4 줄짜리 데코레이터.
# 대부분 use case — 이거 선호. metaclass 는 진짜 필요할 때만.

External links

Exercise

한 클래스에서 만든 모든 인스턴스가 같은 객체가 되게 하는 Singleton 메타클래스를 구현해. 같은 모양을 클래스 데코레이터와 __new__로도 만들어 두 번 생성한 객체가 is로 같은지 확인하고, 어느 구현이 가장 직접적인지 비교해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.