본문 바로가기

Programming/Python

[Python] 메타클래스(Meta Class) 이해하기

파이썬로고

파이썬에서는 모든 것이 객체다.

 

파이썬 프로그램에서는 모든 데이터는 객체 개념을 사용하여 저장됩니다.

가장 기본적인 데이터 타입인 숫자, 문자열, 리스트, 사전 뿐만 아니라 함수, 모듈, 심지어 클래스조차도 객체입니다.

즉, 우리가 작성하는 클래스도 어떤 방식으로든 생성이 되고 관리되는 대상이라는 것입니다.

예를 들어서 아래와 같은 코드를 보면

class Foo:
	pass
    
print(type(sampleClassA)) # <class 'type'>

Foo는 사용자가 정의한 클래스이지만 실제로는 type 이라는 이름의 객체에 의해 생성된 것으로 확인할 수 있습니다.

다시 말해서 클래스 자체도 객체이며, 클래스를 생성하는 다른 존재가 있다는 것을 알 수 있습니다.

이 역할을 하는 것이 바로 메타클래스(Metaclass)입니다.

인스턴스를 만드는 것은 클래스, 이 클래스를 만드는 것은 메타클래스로 클래스를 생성할 때 그 구조나 동작을 제어할 수 있도록 해주는 역할을 합니다.

 

메타클래스 기본

파이썬에서 대부분의 클래스는 type이라는 내장 메타클래스를 통해 생성됩니다.

type()는 보통 객체의 타입을 확인할 때 많이 쓰지만 클래스를 동적으로 생성하는 함수이기도 합니다.

아래와 같은 형태로 클래스를 생성할 수 있습니다.

type(name, base, dct)
  • name : 클래스의 이름 (문자열)
  • bases : 상속할 부모클래스 (튜플로 전달)
  • dict : 클래스에 포함할 속성과 메소드 정의 (사전형태)

실제로는 다음과 같이 사용할 수 있습니다.

def say_hello(self):
    print(f"Hello from {self.name}")

attrs = {
    'name': 'Python',
    'say_hello': say_hello
}
# type함수를 통해 클래스 생성
MyClass = type('MyClass', (object,), attrs)

obj = MyClass()
obj.say_hello()  
# Hello from Python

위의 예제는 'MyClass'라는 이름의 클래스를 생성하고, object를 상속받습니다.

name과 say_hello() 메소드를 속성으로 가지고 있습니다.

MyClass 인스턴스를 생성후에 say_hello() 함수를 호출하면 함수가 의도한 대로 호출되는 것을 확인할 수 있습니다.

위에서 구현한 클래스를 일반적인 방법으로 생성한다면 아래와 같습니다.

class MyClass:
    name = 'Python'

    def say_hello(self):
        print(f"Hello from {self.name}")

obj = MyClass()
obj.say_hello()  # Hello from Python

 

커스텀 메타클래스 생성하기

메타클래스를 이용해서 원하는 방식으로 클래스를 생성하고 제어할 수 있는 커스텀 메타클래스를 생성할 수 있습니다.

커스텀메타클래스는 기본적으로 type 클래스를 상속하여 만들며, __new__() , __init__(), __call()__를 오버라이딩해서 동작을 정의합니다.

메소드 호출 시점 주요 역할
__new__ 클래스를 생성할 직전 클래스 정의 가공 (속성, 메서드 변경)
__init__ 클래스를 생성한 직후 초기화 작업, 등록, 로그 출력 등
__call__ 클래스가 인스턴스화될 때 인스턴스 생성 로직 커스터마이징

 

1. __new__() : 클래스 생성 직전 개입

type.__new__()는 type(name,base,attrs)와 같은 방식으로 호출됩니다.

클래스 정의의 핵심 내용을 조작할 수 있습니다.

def cus_mul(self, d):
    for i in range(len(self)):
        self[i] = self[i]*d

def cus_replace(self,old,new):
    while old in self:
        self[self.index(old)] = new

#MetaClass
class CustomListMeta(type):
    def __new__(metacls, name, bases, attrs):
        print('__new__ -> ', metacls, name, bases, attrs)
        attrs['desc'] = '커스텀 클래스'
        attrs['cus_mul'] = cus_mul
        attrs['cus_replace'] = cus_replace

        return type.__new__(metacls, name, bases, attrs)
        
        ...

cus_mul(), cus_replace()는 인스턴스가 쓸수 있는 custom method입니다.

desc 속성과 위에서 정의한 메소드가 메타클래스를 통해 클래스 정의 시점에 자동으로 클래스에 삽입됩니다.

type.__new__()를 호출해서 실제 클래스가 생성됩니다.

 

2. __init__() : 클래스 생성 직후 개입

__new__함수에서 클래스를 만들어 반환한 후 호출됩니다.

주로 초기화나 클래스 등록 용도로 사용됩니다.

class CustomListMeta(type):
    # 생성된 인스턴스 초기화
    def __init__(self, name, base, dict):
        print('__init__ -> ', self, name,base,dict)
        super().__init__(name,base,dict)
        
   ...

 

3. __call__ : 클래스 인스턴스 생성 시점에 개입

클래스는 기본적으로 __call__() 메소드롤 통해 인스턴스를 생성합니다.

메타클래스에서 이를 오버라이딩해서 인스턴스 생성 과정을 감시하거나 수정할 수 있습니다.

class CustomListMeta(type):
    # 인스턴스 실행
    def __call__(self, *args, **kwargs):
        print('__call__ -> ', self, *args,**kwargs)
        obj = super().__call__(*args,**kwargs)
        return obj 
    
    ...

 

전체 코드는 아래와 같습니다.

def cus_mul(self, d):
    for i in range(len(self)):
        self[i] = self[i]*d

def cus_replace(self,old,new):
    while old in self:
        self[self.index(old)] = new

class CustomListMeta(type):
    def __new__(metacls, name, base, namespace):
        print('__new__ -> ', metacls, name, base, namespace)
        namespace['desc'] = 'CustomList'
        namespace['cus_mul'] = cus_mul
        namespace['cus_replace'] = cus_replace
        return type.__new__(metacls, name, base, namespace)

    def __init__(self, name, base, dict):
        print('__init__ -> ', self, name,base,dict)
        super().__init__(name,base,dict)
    
    def __call__(self, *args, **kwargs):
        print('__call__ -> ', self, *args,**kwargs)
        obj = super().__call__(*args,**kwargs)
        return obj 
    
CustomList = CustomListMeta('CustomList',(list,),{})

c2 = CustomList([1,2,3,4,5,6,7,8,9])
# __new__ ->  <class '__main__.CustomListMeta'> CustomList (<class 'list'>,) {}
# __init__ ->  <class '__main__.CustomList'> CustomList (<class 'list'>,) {'desc': 'CustomList', 'cus_mul': <function cus_mul at 0x0000022426AA8860>, 'cus_replace': <function cus_replace at 0x0000022426AA9120>}
# __call__ ->  <class '__main__.CustomList'> [1, 2, 3, 4, 5, 6, 7, 8, 9]

CustomListMeta로 명명된 메타클래스를 통해 클래스를 생성합니다.

CustomList라는 클래스가 생성되고, 해당클래스는 list를 상속합니다. 클래스 정의는 하지 않았지만 메타 클래스가 desc,cus_mul,cus_replace를 자동으로 삽입합니다

CustomList객체 생성 시에 __call__()이 호출되고, 최종적으로 list 기반 객체가 생성됩니다.

c2.cus_mul(1000)
c2.cus_replace(1000,7777)

print(c2)
# [7777, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000]

c2는 list를 상속하였기 때문에 리스트처럼 동작하지만, cus_mul과 cus_replace 함수를 사용할 수 있습니다.

 

위와 같이 메타클래스에서는 클래스 정의 시점부터 인스턴스 생성 시점까지의 전체 흐름에 관여할 수 있습니다.

 

메타클래스 사용 사례

1. abc 모듈의 ABCMeta - 추상 클래스 강제

파이썬의 표준라이브러리의 abc 모듈은 메타클래스를 이용해 추상 클래스를 지원

구현 강제, 인터페이스 제약을 위해 사용됩니다.

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        print("멍멍")

작동 방식

  • Animal 클래스는 ABCMeta를 메타클래스로 가짐
  • abstractmethod로 지정된 메서드가 구현되지 않으면 인스턴스 생성 불가
  • 메타클래스가 클래스 정의 시점에 추상 메서드 존재 여부를 검사

사용 위치:

  • 공통 인터페이스 강제
  • 프레임워크에서 사용자 정의 클래스 검증 등

2. Django ORM — 모델 클래스 자동 등록 & 필드 해석

Django의 모델 정의 방식은 내부적으로 메타 클래스를 적극 활용

from django.db import models

class BlogPost(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()

작동 방식

  • models.Model의 메타클래스가 ModelBase
  • 모델 클래스가 정의되면:
    • 필드(CharField, TextField)를 수집하여 메타정보로 등록
    • 테이블 이름, 필드 속성 등 자동 추론
  • __new__ 및 __init__ 단계에서 구조 분석과 클래스 등록을 자동화

사용 위치:

  • 클래스 → DB 테이블로 자동 매핑
  • 필드 유효성 검사, 관리자 페이지 자동 생성 등

3. Enum 모듈 — 열거형 자동 등록

from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

작동 방식

  • EnumMeta라는 메타클래스가 클래스 정의 시 RED, GREEN, BLUE를 수집하여 열거형으로 변환
  • 내부적으로 모든 멤버는 자동 등록되고, 순회 가능하게 구성됨

사용 위치:

  • 상수 집합 관리
  • 직렬화, 역직렬화 (ex. JSON ↔ Enum)

실무에서 메타클래스는 다음과 같은 경우에 사용됩니다.

사용 목적 설명
속성 강제 필수 메서드/속성이 있는지 클래스 생성 시 검사
메타데이터 수집 필드, 타입, 메서드 등 클래스 내부 구조를 분석
클래스 등록 사용자 정의 클래스를 자동으로 레지스트리에 등록
선언적 정의 지원 모델/스키마처럼 클래스 = 설정 구조를 가능하게 함

 

이번 글에서는 파이썬의 메타클래스에 대해 처음부터 차근차근 살펴보았습니다.
“클래스를 만드는 클래스”라는 다소 낯선 개념부터, type() 함수의 작동 원리,
그리고 __new__, __init__, __call__ 메서드의 동작 흐름까지 실제 예제와 함께 정리해보았습니다.

메타클래스는 일반적인 개발에서는 자주 쓰이지 않지만,
프레임워크나 라이브러리 수준의 코드를 작성할 때 강력한 도구가 되어줍니다.

'Programming > Python' 카테고리의 다른 글

[Python] Context Manager  (16) 2025.06.24
[Python] Async I/O 정리  (13) 2025.06.21
[Python] Future 이해하기  (2) 2025.06.20
[Python] 코루틴(Coroutine) 이해하기  (13) 2025.06.19
[Python] 클로저 (Closure)  (0) 2025.06.17