0Pricing
Python Academy · 강의

사용자 정의 메타클래스 작성

메타클래스에서 __new__와 __init__을 사용해 클래스 생성을 사용자 지정합니다.

사용자 정의 메타클래스 작성은(는) CoddyKit의 무료 Python Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Python Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Python Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

메타클래스 선언

사용자 지정 메타클래스를 사용하려면 클래스 정의에 metaclass=MyMeta를 전달하세요. 메타클래스는 type을 상속해야 합니다.

class MyMeta(type):
    pass

class Foo(metaclass=MyMeta):
    pass

print(type(Foo))   # <class '__main__.MyMeta'>

__new__ 재정의

__new__(mcs, name, bases, namespace)은 클래스 객체를 만듭니다. 클래스가 생성되기 전에 네임스페이스를 수정할 때 사용하세요.

class UpperAttrMeta(type):
    def __new__(mcs, name, bases, namespace):
        upper = {k.upper(): v for k, v in namespace.items()
                 if not k.startswith("_")}
        return super().__new__(mcs, name, bases, {**namespace, **upper})

class Config(metaclass=UpperAttrMeta):
    debug = True

print(Config.DEBUG)  # True

__init__ 재정의

__init__(cls, name, bases, namespace)은 클래스가 생성된 후에 실행됩니다. 등록과 같은 생성 후 설정에 사용하세요.

_registry = {}

class AutoRegisterMeta(type):
    def __init__(cls, name, bases, namespace):
        super().__init__(name, bases, namespace)
        if bases:   # skip the base class itself
            _registry[name] = cls

class Handler(metaclass=AutoRegisterMeta): pass
class JSONHandler(Handler): pass
class XMLHandler(Handler):  pass

print(_registry)  # {'JSONHandler': ..., 'XMLHandler': ...}

인터페이스 계약 적용

필수 메서드가 클래스 본문에 없으면 __new__에서 TypeError를 발생시키세요.

class InterfaceMeta(type):
    REQUIRED = frozenset({"process", "validate"})

    def __new__(mcs, name, bases, ns):
        if bases and not mcs.REQUIRED.issubset(ns):
            missing = mcs.REQUIRED - ns.keys()
            raise TypeError(f"{name} must define: {missing}")
        return super().__new__(mcs, name, bases, ns)

class Worker(metaclass=InterfaceMeta):
    def process(self): ...
    def validate(self): ...

메타클래스를 통한 Singleton

메타클래스의 __call__을 재정의해 인스턴스 생성을 가로채고 매번 같은 인스턴스를 반환하세요.

class SingletonMeta(type):
    _instances = {}
    def __call__(cls, *a, **kw):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*a, **kw)
        return cls._instances[cls]

class DB(metaclass=SingletonMeta):
    pass

print(DB() is DB())  # True

메서드 동적 추가

메타클래스를 사용하는 모든 클래스에 유틸리티 메서드를 주입하면 각 클래스를 개별적으로 수정하지 않아도 됩니다.

class MethodInjectorMeta(type):
    def __new__(mcs, name, bases, ns):
        ns["describe"] = lambda self: f"I am {type(self).__name__}"
        return super().__new__(mcs, name, bases, ns)

class Widget(metaclass=MethodInjectorMeta):
    pass

print(Widget().describe())   # I am Widget

정렬된 네임스페이스를 위한 __prepare__

__prepare__를 재정의해 사용자 지정 매핑을 클래스 네임스페이스로 제공하세요. 예를 들어 정의 순서를 보존하거나 중복을 감지할 때 사용할 수 있습니다.

class NoDuplicatesMeta(type):
    class _NS(dict):
        def __setitem__(self, k, v):
            if k in self and not k.startswith("_"):
                raise ValueError(f"Duplicate attribute: {k}")
            super().__setitem__(k, v)

    @classmethod
    def __prepare__(mcs, name, bases):
        return mcs._NS()

메타클래스 충돌 해결

기반 클래스가 여러 개이고 서로 다른 메타클래스를 사용하면 Python은 가장 파생된 메타클래스를 명시적으로 사용하도록 요구합니다. 그렇지 않으면 충돌 오류가 발생합니다.

class MetaA(type): pass
class MetaB(type): pass

# class Bad(A, B): pass  # TypeError: metaclass conflict

class MetaAB(MetaA, MetaB): pass   # resolve manually

class A(metaclass=MetaA): pass
class B(metaclass=MetaB): pass
class Good(A, B, metaclass=MetaAB): pass

메타클래스와 __init_subclass__ 비교

서브클래스 등록과 간단한 훅에는 __init_subclass__가 더 간단하며 메타클래스가 필요하지 않습니다.

class Base:
    _registry = []
    def __init_subclass__(cls, **kw):
        super().__init_subclass__(**kw)
        Base._registry.append(cls)

class ChildA(Base): pass
class ChildB(Base): pass

print(Base._registry)  # [ChildA, ChildB]

속성 접근 추적

메타클래스에서 __getattribute__를 재정의하면 클래스의 속성 접근을 가로챌 수 있습니다(인스턴스의 속성 접근이 아닙니다).

class TraceMeta(type):
    def __getattribute__(cls, name):
        val = super().__getattribute__(name)
        if not name.startswith("_"):
            print(f"Accessing class attr: {name}")
        return val

class Config(metaclass=TraceMeta):
    HOST = "localhost"

_ = Config.HOST   # logs: Accessing class attr: HOST

ORM 필드 수집 패턴

Django 스타일 ORM은 메타클래스를 사용해 클래스 본문의 Field 디스크립터를 _meta 레지스트리로 수집합니다.

class Field:
    def __init__(self, col): self.col = col

class ModelMeta(type):
    def __new__(mcs, name, bases, ns):
        fields = {k: v for k, v in ns.items() if isinstance(v, Field)}
        ns["_fields"] = fields
        return super().__new__(mcs, name, bases, ns)

class User(metaclass=ModelMeta):
    name = Field("name")
    email = Field("email")

print(User._fields.keys())  # dict_keys(['name', 'email'])

빠른 확인

사용자 지정 메타클래스에서 클래스 객체 자체를 생성하는 메서드는 무엇입니까(__init__ 전에 호출됨)?

복습

사용자 지정 메타클래스는 __new__/__init__을 재정의해 클래스 생성을 제어합니다. 계약 적용, 서브클래스 자동 등록, 메서드 주입 또는 필드 디스크립터 수집에 사용할 수 있습니다. 간단한 등록이 필요할 때는 __init_subclass__를 우선 사용하세요.

자주 묻는 질문

“사용자 정의 메타클래스 작성” 강의는 무료인가요?

네 — “사용자 정의 메타클래스 작성” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Python Academy 강의 전체를 잠금 해제할 수 있습니다. Python Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“사용자 정의 메타클래스 작성”에서 뭘 배우나요?

메타클래스에서 __new__와 __init__을 사용해 클래스 생성을 사용자 지정합니다. 브라우저에서 직접 실행하는 실습 코드로 Python Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Python Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Python Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“사용자 정의 메타클래스 작성” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Python Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Python Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. Python 클래스가 생성되는 방식
  2. 사용자 정의 메타클래스 작성
  3. 디스크립터: __get__, __set__, __delete__
  4. __slots__와 메모리 최적화
← Python Academy(으)로 돌아가기