2017-12-08 15 views
1

최근에 저는 싱글 톤을 구현하기 위해 메타 클래스를 사용했습니다. 그럼,이 __metaclass__ 물건이 어떻게 작동하는지 이해하려고했습니다. 나는 그것에 대해 두 가지 질문이은 메타 클래스가 매우 비슷합니다.

import numpy as np 

class Meta(type): 
    _instance = None 
    def __init__(cls, *args, **kwargs): 
     print('meta: init') 
     #return super(Meta, cls).__init__(*args, **kwargs) 

    def __new__(cls, *args, **kwargs): 
     print('meta: new') 
     return super(Meta, cls).__new__(cls, *args, **kwargs) 

    def __call__(cls, *args, **kwargs): 
     print('meta: call') 
     if cls._instance is None: 
      cls._instance = super(Meta, cls).__call__(*args, **kwargs) 
     print(cls._instance.__class__) 
     return cls._instance 



class ClassA(): 

    __metaclass__ = Meta 
    def __init__(self, *args, **kwargs): 
     self.val = np.random.randint(1000) 
     print('classA: init') 


    def __new__(cls, *args, **kwargs): 
     print('classA: new') 
     return super(ClassA, cls).__new__(cls, *args, **kwargs) 

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

class ClassB(): 

    __metaclass__ = Meta 
    def __init__(self, *args, **kwargs): 
     print('classB: init') 
     self.val = np.random.randint(1000) 

    def __new__(cls, *args, **kwargs): 
     print('classB: new') 
     return super(ClassB, cls).__new__(cls, *args, **kwargs) 

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


class ClassC(ClassB): 

    def __init__(self, *args, **kwargs): 
     print('classC: init') 

     super(ClassC, self).__init__(self, *args, **kwargs) 
     self.test = 3 

    def __new__(cls, *args, **kwargs): 
     print('classC: new') 
     return super(ClassC, cls).__new__(cls, *args, **kwargs) 

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



if __name__ == '__main__': 


    a1 = ClassA() 
    b1 = ClassB() 
    a2 = ClassA() 
    b2 = ClassB() 
    c1 = ClassC() 

    print(a1.val) 
    print(b1.val) 
    print(a2.val) 
    print(b2.val) 

: 이렇게하려면, 나는이 코드 조각을 썼다

  • 이유는 무엇입니까 cls._instance__main__.ClassA object (또는 __main__.ClassA object)? super으로 생성되었으므로 상위 인스턴스가 아니어야합니까?

  • ClassC 예를하지만를 만들 때 왜 ClassC 내부에 아무것도 (도 __call____init__)가 호출되지 경우에만 ClassB.__call__ (그것에서 상속으로)?

+0

'__super__'란 무엇입니까? 파이썬에는 특별한 방법이나 속성이 없습니다. 당신은'super()'객체에 대해 이야기하고 있습니까? –

+0

Python 2.x 또는 3.x를 사용하고 있습니까? –

답변

1

첫째, 여기에는 가독성을 위해 제거 된 일부 "노이즈"가있는 코드의 줄 바꿈 버전이 나와 있습니다. 이제

import random 

class Meta(type): 
    _instance = None 

    def __call__(cls, *args, **kwargs): 
     print('meta: call: %s' % cls) 
     if cls._instance is None: 
      cls._instance = super(Meta, cls).__call__(*args, **kwargs) 
     print("meta: call: returning %[email protected]%s" % (cls._instance.__class__, id(cls._instance))) 
     return cls._instance 


class ClassA(object): 
    __metaclass__ = Meta 

    def __new__(cls, *args, **kwargs): 
     print('classA: new') 
     return super(ClassA, cls).__new__(cls, *args, **kwargs) 

    def __init__(self, *args, **kwargs): 
     self.val = random.randint(1, 1000) 
     print('classA: init') 


class ClassB(object): 
    __metaclass__ = Meta 

    def __new__(cls, *args, **kwargs): 
     print('classB: new') 
     return super(ClassB, cls).__new__(cls, *args, **kwargs) 

    def __init__(self, *args, **kwargs): 
     print('classB: init') 
     self.val = random.randint(1, 1000) 

class ClassC(ClassB): 
    def __new__(cls, *args, **kwargs): 
     print('classC: new') 
     return super(ClassC, cls).__new__(cls, *args, **kwargs) 

    def __init__(self, *args, **kwargs): 
     print('classC: init') 
     super(ClassC, self).__init__(self, *args, **kwargs) 
     self.test = 3 


def main(): 
    a1 = ClassA() 
    b1 = ClassB() 
    a2 = ClassA() 
    b2 = ClassB() 
    c1 = ClassC() 

    print(a1.val) 
    print(b1.val) 
    print(a2.val) 
    print(b2.val) 

if __name__ == '__main__': 
    main() 

는 질문 :

이유는 무엇입니까 cls._instance입니다 주요 .ClassA 개체 (또는 .ClassA 개체)? super로 생성 되었기 때문에 부모 인스턴스가 아니어야합니까?

아니요, 왜요? 귀하의 경우, ClassA()을 호출 할 때 실제로는 Meta.__call__(ClassA)을 호출합니다. super(Meta, cls)은 인 다음 클래스를 선택합니다. type이므로 type.__call__(Meta, ClassA)을 호출합니다. type.__call__(Meta, ClassA)ClassA.__new__()ClassA.__init__()을 차례로 호출합니다. 당연히 (물론 상속을 거의 사용할 수 없으면) ClassA 인스턴스를 다시 얻습니다.

일반적으로

: 여전히 self을 가리 킵니다 super(CurrentClass, obj_or_cls).something()합니다 (__mro__의 다음 수업 시간에 실제로)은 "부모"클래스의 something 구현을 선택되지만 self (또는 cls 또는 무엇이든) 첫 번째 인수 (또는 cls 기타). IOW, 메서드의 부모 구현을 선택하고 있지만이 메서드는 여전히 첫 번째 인수로 현재 개체/클래스를 가져옵니다. ClassC 인스턴스하지만 단지 ClassB.__call__을 (그것에서 상속으로) 만들 때 ClassC 내부에 아무것도 (__call____init__도)가 호출되지 왜

ClassC.__call__() 호출되지 않습니다 때문에 코드의 통화에서 아무것도 그것. ClassC 인스턴스를 담당하는 것은 ClassC.__metaclass__.__call__()입니다. 인스턴스ClassC 일 때 ClassC.__call__()이 호출되어야합니다.

ClassC.__init__() 실제로는 ClassC 인스턴스가 생성되지 않았기 때문에 호출되지 않습니다. 첫 번째 인스턴스는 ClassB입니다.이 시점에서 (ClassB의 첫 번째 인스턴스) ClassB에는 속성이 없습니다. _instance이므로 해당 속성은 상위 클래스 object에서 조회됩니다. object에는 _instance 속성이 없기 때문에 ClassB.__class__에있는 검색은 Meta에서 계속됩니다. 이 인스턴스에는 _instance 속성 (None)이 있으므로 ClassB 인스턴스를 만들고 ClassB._instance (ClassB.__dict__에 속성을 만드는 중개자)에 바인딩합니다.

ClassC을 설치하려고 시도하면 if cls._instance is None 테스트는 _instanceClassC에 먼저 찾습니다. 이 시점에서 ClassC이 아니고_instance이라는 특성을 가지고 있으므로 ClassC 첫 번째 부모 (다음 클래스의 mro)는 ClassB입니다. 이미 ClassB을 인스턴스화 했으므로 조회가 여기에서 끝나고 ClassC._instance에서 ClassB.__dict__["_instance"]으로 바뀌므로 은 이미 ClassB 인스턴스 인입니다. 당신이 작동 구현을 원하는 경우에

, 당신은 Meta._instance 제거해야하고 Meta.__init__()에서 각 클래스에 None_instance 설정 중 하나를

class Meta(type): 
    def __init__(cls, *args, **kw): 
     cls._instance = None 

    def __call__(cls, *args, **kwargs): 
     print('meta: call: %s' % cls) 
     if cls._instance is None: 
      cls._instance = super(Meta, cls).__call__(*args, **kwargs) 
     print("meta: call: returning %[email protected]%s" % (cls._instance.__class__, id(cls._instance))) 
     return cls._instance 

또는 getattr()으로 Meta.__call__()에서 시험을 대체 :

class Meta(type): 

    def __call__(cls, *args, **kwargs): 
     print('meta: call: %s' % cls) 
     if not getattr(cls, "_instance", None): 
      cls._instance = super(Meta, cls).__call__(*args, **kwargs) 
     print("meta: call: returning %[email protected]%s" % (cls._instance.__class__, id(cls._instance))) 
     return cls._instance