4-3. Meta Class(3)

이전 강의에서 배운 내용을 실습 위주로 정리해보자

Mata Class

  • 오늘의 키워드
    • Type inheritance
      타입을 상속하는 타입클래스는 바로 메타클래스다.
    • Custom Meta Class (오늘의 핵심)
      타입클래스를 상속 받으면 우리만의 커스텀 메타 클래스를 만들 수 있다.
    • 메타 클래스 상속 실습

메타 클래스를 상속한다는게 무슨 의미가 있나요? 🤔

➢ 메타 클래스를 상속한다는 것은 type 클래스를 상속한다는 것이다.

➢ type 클래스를 상속 받고, 객체가 인스턴스화 될때 타입의 내부적으로 호출되는 함수가 있다.

이 함수를 이용해서 구조적으로 초기화 되는 시점에 우리가 원하는 코드를 작성할 수 있다.

➢ 이를 이용해서 커스텀 메타 클래스를 생성할 수 있다.

  • 클래스 생성을 가로채기 (__new____init__ 등 )
  • 클래스 수정하기 ( modify)
  • 클래스 개선하기 (기능 추가)
  • 수정된 클래스 반환하기

커스텀 메타클래스 생성 예제 (type 함수 상속 X)

클래스 바깥에 정의된 메소드를 가져다 쓰는 커스텀 메타 클래스를 만드려고 한다.
그런데 이때 메소드는 self를 이용하게 만들었다.
self는 인스턴스 자신을 의미하므로, 즉 self는 시퀀스형 데이터가 될 것이다.

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

type 함수를 이용해서 배열 자료형 ; list를 상속받는 메타 클래스를 만들어보자.

CustomList1 = type(
    'CustomList1',
    (list,),
    {  
        'desc': '커스텀 리스트1',
        'cus_mul': cus_mul,
        'cus_replace': cus_replace,
    }
)

c1 = CustomList1([1, 2, 4, 5, 3, 3])

커스텀 메타 클래스 CustomList1list 를 상속 받았기 때문에,
이를 인스턴스화 할때 배열을 인자로 넣어줌으로서 self는 우리가 입력한 배열이 되었다.

c1.cus_mul(1000)
print('ex 1>', c1)
# ex 1> [1000,2000,4000,5000,3000,3000]

# 3000을 haha로 바꾸겠다.
c1.cus_replace(3000, 'haha')
print('ex 1>', c1)
# ex 1> [1000,2000,4000,5000,'haha','haha']

print('ex 1>', c1.desc)
# ex 1> 커스텀 리스트1

print('ex 1>', dir(c1))
# 우리가 추가한 메소드와
# 리스트에서 사용할 수 있는 내장함수들이 포함되어 있다.
# ['append', 'sort', ..., 'cus_mul', 'cus_replace', 'desc' ]

커스텀 메타클래스 생성 예제 (type 함수 상속 O)

type 함수를 상속 받아 직접 커스텀 메타클래스를 만듦으로서,
예제 1번에서 보이지 않았던 내부 처리 과정을 볼 수 있다.

3가지 함수를 이용해서 조금 더 디테일하게 커스터마이즈 할 수 있게 된다.

type 함수를 상속할 때 호출되는 3가지 함수

  • new -> init -> call 순서로 호출된다.
  • new, call은 return을 해주어야 한다.
  • __new__
    클래스 인스턴스를 생성한다. (메모리 초기화)
    클래스 생성 시점에 인자 4개가 넘어오게 만든다.
    • metaclass : 메타클래스의 이름
    • name, bases, namespace : type 함수와 같다.
  • __init__
    __new__ 에서 생성된 인스턴스를 초기화한다.
    인자 4개가 넘어온다.
    • self : 인스턴스의 이름
    • name, bases, namespace : type 함수와 같다.
  • __call__
    인스턴스를 실행한다.
    인자 3개가 넘어온다.
    • self : 인스턴스의 이름
    • *args : 입력 받은 여러개의 인자를 접근할 수 있다.
    • **kwargs : key=value 형태로 입력 받은 인자를 딕셔너리 형태로 접근가능하다.

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

# type을 상속받은 메타 클래스 CustomListMeta
class CustomListMeta(type):
    def __init__(self, object_or_name, bases, dict):
        print('ex2 > __init__ ->', self, object_or_name, bases, dict)
        super().__init__(object_or_name, bases, dict)

    def __call__(self, *args, **kwargs):
        print('ex2 > __call__ ->', self, *args, **kwargs)
        return super().__call__(*args, **kwargs)

    def __new__(metacls, name, bases, namespace):
        print('ex2 > __new__ ->', metacls, name, bases, namespace)
        namespace['desc'] = '커스텀 리스트 2'
        namespace['cus_mul'] = cus_mul  # 재사용
        namespace['cus_replace'] = cus_replace

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

CustomList2 = CustomListMeta(
    'CustomList2',
    (list,),
    {}
)

예제 1번과 동일하게 동작한다.

c2 = CustomList2([1, 2, 3, 4, 5, 6, 7, 8])
c2.cus_mul(1000)
c2.cus_replace(1000, 7777)

print('ex 2>', c2)
print('ex 2>', c2.desc)

# 상속 확인
print(CustomList2.__mro__)
# 왼쪽이 오른쪽을 상속받은 것
#(<class '__main__.CustomList2'>, <class 'list'>, <class 'object'>)

정리

  • 클래스 바깥에 정의된 self를 쓰는 메소드는 컴포넌트화 되어,
    메타 클래스 내부의 메소드에서 사용될 수 있다.
  • type 을 상속 받고, 내부 함수인 __new__, __init__, __call__ 을 이용해서 메타클래스를 조금 더 체계적으로 커스터마이즈 할 수 있다.

[출처]

인프런 - 모두를 위한 파이썬 : 필수 문법 배우기 Feat. 오픈소스 패키지 배포 (Inflearn Original)

좋은 웹페이지 즐겨찾기