1. 파이썬의 메타 클래스
- 파이썬의 메타클래스(= 클래스의 원형)는 type() 클래스
- type()클래스의 원형(= 메타 클래스) 역시 type() 클래스
- Python에서 모든 것은 객체이고, 모든 객체는 클래스의 인스턴스이거나 메타클래스의 인스턴스입니다. type을 제외하고 말이죠.
- type은 실제로 그 자체의 메타클래스(its own meta class)입니다
# Ex 1
class SampleA():
pass
obj1 = SampleA() # obj에 할당한 순간, class SampleA를 인스턴스화 (obj1 : 인스턴스)
print("Ex 1 > ", obj1.__class__) # instance의 속성
# >>> Ex 1 > <class '__main__.SampleA'>
print('Ex 1 > ', type(obj1))
# >>> Ex 1 > <class '__main__.SampleA'>
print("Ex 1 > ", obj1.__class__.__class__)
# >>> Ex 1 > <class 'type'>
print("Ex 1 > ", obj1.__class__ is type(obj1))
# >>> Ex 1 > True
print("Ex 1 > ", obj1.__class__.__class__ is type(obj1).__class__)
# >>> Ex 1 > True
print("Ex 1 > ", type.__class__) # 핵심: type()의 클래스도 type()이다
# >>> Ex 1 > <class 'type'>
기억해둘 것
- 파이썬에서 모든 클래스의 메타 클래스는 type 함수
- 현실의 사물을 클래스 형태로 제작하는 것: 객체지향
- 파이썬에선 클래스와 객체를 같은 의미로 사용 ( class == object )
- 어떤 변수, 클래스, 인스턴스의 원형 : type.
- 원형(type)의 원형: type
# Ex2
n = 10
d = {'a': 10, 'b': 20}
class SampleB():
pass
obj2 = SampleB()
for o in (n, d, obj2):
print('Ex2 > {}, {}, {}'.format(type(o), type(o) is o.__class__, o.__class__.__class__))
# >>> Ex2 > <class 'int'>, True, <class 'type'>
# >>> Ex2 > <class 'dict'>, True, <class 'type'>
# >>> Ex2 > <class '__main__.SampleB'>, True, <class 'type'>
for t in int, float, list, tuple:
print('Ex 2 > ', t, ' : ', type(t))
# >>> Ex 2 > <class 'int'> : <class 'type'>
# >>> Ex 2 > <class 'float'> : <class 'type'>
# >>> Ex 2 > <class 'list'> : <class 'type'>
# >>> Ex 2 > <class 'tuple'> : <class 'type'>
print('Ex 2 > ', type(type))
# >>> Ex 2 > <class 'type'>
2. 메타클래스 동적 생성
- 동적 생성한 메타클래스 -> 커스텀 메타클래스를 생성할 수 있다.
- 의도하는 방향으로 직접 클래스 생성에 관여할 수 있는 큰 장점
< type 동적 클래스 생성 >
type함수는 세가지 인자를 받는다 : Name(이름), Bases(상속), Dct(속성, 메소드)
- Name: 말 그대로 이름, 클래스명으로 메타클래스의 이름이 된다.
- Bases: 상속받을 자료형으로, 튜플형태로 여러 자료형을 받을 수 있다 => (list, ) or (set, )
- Dct: 속성 or 메서드를 입력. 클래스 내부 메서드를 생성한다고 생각하면 된다.
# Ex 1
class Sample1():
pass
s1 = type('Sample1', (), {}) # base, dct를 빈값으로 넣었다
print('Ex 1 > ', s1)
print('Ex 1 > ', type(s1)) # type meta class의 meta class
print('Ex 1 > ', s1.__base__)
# 클래스의 base는 object --> 모든 클래스는 object를 상속받는다
print('Ex 1 > ', s1.__dict__)
# dynamic 생성 + 상속
class Parent1:
pass
s2 = type('Sample2', (Parent1,), dict(attr1=100, attr2='hi'))
# 2번째 인자: 튜플형태 -> 여러인자를 받을 수 잇따.
# key-value형식으로 넣은 것이 아니기 때문에, key에 따옴표 안붙인다.
print('Ex 2 > ', s2)
print('Ex 2 > ', type(s2))
print('Ex 2 > ', s2.__base__)
print('Ex 2 > ', s2.__dict__)
print('Ex 2 > ', s2.attr1, s2.attr2) # 100, hi에 접근 가능
# 위와 같은 방식으로 클래스를 생성할 수 있다.
# 아래 형태의 클래스를 위에서 만든 것
# class Sample2():
# attr1 = 10
# attr2 = 'hi'
type 동적 클래스 + 메소드 생성
# Ex2
# type 동적 클래스 생성 + 메소드
class SampleEx:
attr1 = 30
attr2 = 100
def add(self, m, n):
return m + n
def mul(self, m, n):
return m * n
# 일반적인 클래스 및 내부 메서드 생성
# 위 내용을 아래처럼 동적으로 생성할 수 있다.
s3 = type('Sample3',
(object,),
dict(attr1=30, attr2=100, add=lambda x, y: x + y, mul=lambda x, y: x * y)
# {'attr1': 30, 'attr2': 100, 'add':lambda x, y: x + y, 'mul':lambda x, y: x * y}
)
print('Ex 2 > ', s3.attr1)
print('Ex 2 > ', s3.attr2)
print('Ex 2 > ', s3.add(100, 200))
print('Ex 2 > ', s3.mul(10, 20))
# python low level에서 메소드를 조작하기 위해 배운다.
3. 메타클래스 상속
- type 클래스 상속 (metaclass를 상속한다는 뜻은 type class를 상속한다는 뜻)
- metaclass 속성 사용
- Custom Metaclass 생성
- class 생성의 hooking 가능 (intercept)
- class 수정 (modify, 기능 추가)
custom metaclass 생성 예제 (type 상속x)
# Ex 1
def cus_mul(self, d):
for i in range(len(self)):
self[i] = self[i] * d
# 상용적으로 사용하는 replace()메소드를 만든다고 생각해보자.
def cus_replace(self, old, new):
while old in self:
self[self.index(old)] = new #self는 list [1, 2, 3 .., 6] 자체가 된다.
# list를 상속받음, 메소드 2개(cus_mul, cus_replace) 추가
CustomList1 = type('CustomList1',
(list,), # list 클래스를 상속받는다 => CustomList1은 list의 모든 메소드를 사용할 수 있게된다.
{
'desc': '커스텀 리스트1',
'cus_mul': cus_mul,
'cus_replace': cus_replace
})
CustomList1라는 클래스를 메타로 찍어놓은 것 (붕어빵 만드는 틀을 찍어놓은 것)
bases로 (list)를 상속받았기에 가능한 일 (list 말고 다른 자료형을 사용하여 응용 가능)
c1 = CustomList1([1, 2, 3, 4, 5, 6]) # 여기의 리스트가 곧 self가 된다.(초기화 값)
print('Ex 1 > ', c1)
>>> Ex 1 > [1, 2, 3, 4, 5, 6]
c1.cus_mul(1000)
print('Ex 1 > ', c1)
>>> Ex 1 > [1000, 2000, 3000, 4000, 5000, 6000]
c1.cus_replace(3000, 3)
print('Ex 1 > ', c1)
>>> Ex 1 > [1000, 2000, 3, 4000, 5000, 6000]
print('Ex 1 > ', c1.desc)
>>> Ex 1 > 커스텀 리스트1
메소드를 클래스 외부로 빼 놓았을 때의 이점:
메소드를 컴포넌트식으로(조립식으로) 언제든지 끼우거나 빼서 사용할 수 있다.
커스텀 메타클래스 생성 예제( type 상속 O )
- new -> init -> call 순서로 메소드가 실행된다.
- 인스턴스를 생성하고(new) -> 생성된 인스턴스를 초기화 후(init) -> 호출(call)
class CustomListMeta(type):
# 생성된 인스턴스 초기화 <2>
def __init__(self, object_or_name, bases, dict):
print('__init__ -> ', self, object_or_name, bases, dict)
super().__init__(object_or_name, bases, dict) # 부모한테 넘겨준다 3개의 인자를
# 인스턴스 호출 <3>
def __call__(self, *args, **kwargs):
print('__call__ -> ', self, *args, **kwargs)
return super().__call__(*args, **kwargs)
# 클래스 인스턴스 생성(메모리 초기화) <1> (namespaces 인자는 속성, 메서드를 받는다)
def __new__(metacls, name, bases, namespaces):
print('__new__ -> ', metacls, name, bases, namespaces)
namespaces['desc'] = 'customlist2'
namespaces['cus_mul'] = cus_mul
namespaces['cus_replace'] = cus_replace
return type.__new__(metacls, name, bases, namespaces)
# type함수를 상속받지 않은 커스텀 메타클래스 생성의 내부 프로세스가 위와 같다.
CustomList2 = CustomListMeta('CustomList2',
(list,),
{}
)
>>> __new__ -> <class '__main__.CustomListMeta'> CustomList2 (<class 'list'>,) {}
>>> __init__ -> <class '__main__.CustomList2'> CustomList2 (<class 'list'>,) {'desc': 'customlist2', 'cus_mul': <function cus_mul at 0x101fb8d30>, 'cus_replace': <function cus_replace at 0x101fb8dc0>}
c2 = CustomList2([1,2,3,4,5])
>>> __call__ -> <class '__main__.CustomList2'> [1, 2, 3, 4, 5]
c2.cus_mul(1000)
c2.cus_replace(1000, 7777)
print('Ex 2 > ', c2)
>>> Ex 2 > [7777, 2000, 3000, 4000, 5000]
print('Ex 2 > ', c2.desc)
>>> Ex 2 > customlist2
메타클래스는 99%의 사용자는 전혀 고려할 필요가 없는 흑마법입니다.
당신이 이게 정말 필요할지에 의문을 갖는다면, 필요하지 않습니다.
이게 진짜로 필요한 사람은 그 필요를 알고 있으면서, 왜 필요한지에 대해 설명할 필요가 없는 사람들입니다.
- Tim Peters
모두를 위한 파이썬 : 필수 문법 배우기 Feat. 오픈소스 패키지 배포 (Inflearn Original)