티스토리 뷰

python

python3 metaclass(메타클래스)

insung151 2019. 1. 21. 22:06

Meta class

파이썬에서 object는 "class"라 불리는 object에 의해 생성된다. 즉 다시말해 class도 object이다.

class도 object이기 때문에 object와 같은 방식으로 필드를 추가하거나 삭제하는 연산이 가능하다.

class MyClass:
   pass

obj = MyClass()

MyClass.val = 3
MyClass.print = lambda self : print("Hello")
obj.print()

----------------------------------
Hello

다만 차이점은 class의 필드를 수정하면 이미 생성되 있던 instance도 포함해서 모든 instance들이 같이 수정된다.

이러한 class object를 만들어내는 또 다른 special object를 metaclass 라고 한다. 파이썬에서 디폴트 metaclasstype이다. metaclass가 어떻게 class object를 생성하는지 살펴보자.

아마 파이썬을 공부하면서 type을 본적이 있을 것이다. type은 인자가 하나일 때는 instance의 클래스 정보를 반환한다. 하지만 인자가 3개가 되면 새로운 class object를 만든다. 아래에서 확인해 보자.

class MyClass:
   pass
MyClass = type('MyClass', (), {})

위의 두 코드는 완전히 같은 일을 한다. 위의 type은 3개의 인자를 받고있다. 첫번째 인자는 class의 이름, 두번째 인자는 상속받을 클래스리스트, 세번째는 메서드나 필드같은 namespace를 dictionary 형태로 받는다. 즉 다음의 코드는 int클래스를 상속받으면서 x라는 필드를 갖는 class object가 생성된다

MyInt = type('MyInt', (int,), {'x': 3})
y = MyInt(0)
print(y.x)

----------------------------------
3

이제 metaclass를 정의하고 사용하는 법에 대해 더 알아보자. metaclass는 위에서 본 type을 상속받아서 만들 수 있다.

class MyMetaClass(type):
   def __init__(cls, name, bases, namespace):
       super(MyMetaClass, cls).__init__(name, bases, namespace)
       cls.meta_function = lambda self: print("Hello")

class MyClass(metaclass=MyMetaClass):
   pass

obj = MyClass()
obj.meta_function()

-------------------------------------
Hello
       

MyMetaClass를 만들어 MyClass에서 hooking하고 있다. python3에서는 위와 같이 class를 정의할 때 metaclass를 인자로 넘겨주는 방식으로 hooking한다. (python2는 다른 방식으로한다.)

metaclass__new__,__init__, __call__ 에 대해 살펴보자

class MyMetaClass(type):
       def __new__(cls, *args, **kwargs):
               print('__new__')
               return super().__new__(cls, *args, **kwargs)

       def __init__(cls, *args, **kwargs):
               print('__init__')
               super().__init__(*args, **kwargs)

       def __call__(cls, *args, **kwargs):
               print('__call__')
               return super().__call__(*args, **kwargs)


class MyClass(metaclass=MyMetaClass):
       pass

print("##############")

obj = MyClass()

---------------------------
__new__
__init__
##############
__call__

MyClass가 생성될때 MyMetaClass의 __new__ 가 먼저 실행되고 그 다음에 __init__이 실행되고 있다.

MyClass의 insctance를 생성할때에는 __call__이 실행된다.

metaclass를 이용한 singleton구현

class Singleton(type):

      def __call__(cls, *args, **kwargs):
              if not hasattr(cls, 'instance'):
                      cls.instance = super().__call__(*args, **kwargs)
              return cls.instance


class MyClass(metaclass=Singleton):
      pass


a = MyClass()
b = MyClass()
c = MyClass()
print(a is b)
print(b is c)

-------------------------------
True
True


'python' 카테고리의 다른 글

python3 iterator (이터레이터)  (0) 2019.01.09
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함