인프런 리프 - 파이썬 고급 week3 (1)

pyhoo·2021년 3월 27일
0

Inflearn Leaf 2nd

목록 보기
2/5
post-thumbnail

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'>

기억해둘 것

  1. 파이썬에서 모든 클래스의 메타 클래스는 type 함수
  2. 현실의 사물을 클래스 형태로 제작하는 것: 객체지향
  3. 파이썬에선 클래스와 객체를 같은 의미로 사용 ( class == object )
  4. 어떤 변수, 클래스, 인스턴스의 원형 : type.
  5. 원형(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. 메타클래스 동적 생성

  1. 동적 생성한 메타클래스 -> 커스텀 메타클래스를 생성할 수 있다.
  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. 메타클래스 상속

  1. type 클래스 상속 (metaclass를 상속한다는 뜻은 type class를 상속한다는 뜻)
  2. metaclass 속성 사용
  3. 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)

0개의 댓글