2012-08-04 5 views
5

최근에 나는 an interesting discussion on how to make a singleton in Python을 읽었습니다. 솔루션의 하나는 tricky decorator defining a class inside its code as a substitute for decorated class이었다왜 여기에서 재귀가 발생합니까?

def singleton(class_): 
    class class_w(class_): 
     _instance = None 
     def __new__(class2, *args, **kwargs): 
      if class_w._instance is None: 
       class_w._instance = super(class_w, class2).__new__(class2, *args, **kwargs) 
       class_w._instance._sealed = False 
      return class_w._instance 
     def __init__(self, *args, **kwargs): 
      if self._sealed: 
       return 
      super(class_w, self).__init__(*args, **kwargs) 
      self._sealed = True 
    class_w.__name__ = class_.__name__ 
    return class_w 

@singleton 
class MyClass(object): 
    def __init__(self, text): 
     print text 
    @classmethod 
    def name(class_): 
     print class_.__name__ 

x = MyClass(111) 
x.name() 
y = MyClass(222) 
print id(x) == id(y) 

출력은 다음과 같습니다

111  # the __init__ is called only on the 1st time 
MyClass # the __name__ is preserved 
True # this is actually the same instance 

그것은 언급, 우리는 MyClass__init__ 내부 super(MyClass, self).__init__(text)를 사용하는 경우, 우리는 재귀로 얻을.

테스트를 거쳤으며 실제로 재귀가 발생합니다. 하지만, 내가 이해, MyClass 그래서 super(MyClass, self) 그냥 단순히 object해야한다, object 상속하지만 super(MyClass, self)__main__.MyClass

당신이 재귀 일이 왜 나에게 이유를 이해하는 단계에 의해 여기 단계를 상황에 대해 설명 할 수 있음을 밝혀 ?

+1

Super()는 python에서 약간 까다 롭습니다. http://rhettinger.wordpress.com/2011/05/26/super-considered-super/ – Lycha

답변

5

문제는 super(MyClass, self).__init__(text)을 쓰면 super이 호출 될 때 MyClass 클래스가 참조하는 클래스에 관계없이 super를 사용한다는 것입니다. 그러나 장식자는 MyClass을 그 자체의 서브 클래스로 대체합니다. 따라서 원래 __init__ 메소드가 호출 될 때 MyClass은 실제로 실행 메소드를 정의하는 클래스의 서브 클래스를 참조합니다.

단계별로 말하자면, 원본 클래스 (소스로 작성된 것) OrigMyClass과 결과 버전 (데코레이터 뒤) DecMyClass을 호출 할 것입니다. 실행 중에 그 의미가 변경되기 때문에 변수로 그냥 MyClass을 사용합니다.

  1. 당신은 OrigMyClass상의 __init__ 메소드를 정의하지만 __init__ 방법은 super(MyClass, self)하지 super(OrigMyClass, self) 호출합니다. 따라서 실제로 호출 할 메소드는이라고 할 때 MyClass을 가리키는 지에 달려 있습니다. MyClass의 값은 다른 변수와 마찬가지로 실행 시간에 조회됩니다. super 호출 또는 __init__ 메서드 내부에 배치하면 마술처럼 클래스에 바인딩 할 필요가 없습니다. 함수의 변수는 정의 될 때가 아니라 호출 될 때 평가됩니다.

  2. 데코레이터가 실행됩니다. 데코레이터는 새로운 클래스 DecMyClassOrigMyClass의 하위 클래스로 정의합니다. DecMyClasssuper(DecMyClass, self)을 호출하는 __init__을 정의합니다.

  3. 데코레이터가 실행 된 후 MyClass 이름은 DecMyClass 클래스에 바인딩됩니다. 이는 나중에 super(MyClass, self) 호출이 실행될 때 super(DecMyClass, self)을 수행한다는 것을 의미합니다.

  4. MyClass(111)을 수행 할 때 의 개체를 인스턴스화합니다. DecMyClass.__init__super(DecMyClass, self).__init__입니다. 그러면 OrigMyClass.__init__이 실행됩니다.

  5. OrigMyClass.__init__super(MyClass, self).__init__으로 전화하십시오.MyClassDecMyClass을 나타내므로 super(DecMyClass, self).__init__과 같습니다. 그러나 DecMyClassOrigMyClass의 하위 클래스입니다. 요점은 MyClass이 을 나타내므로 OrigMyClass은 실제로는의 하위 클래스에서 수퍼 을 호출하는 것입니다.

  6. 따라서 은 다시 자신을 호출하는 등 OrigMyClass.__init__을 무한대로 호출합니다.

    >>> class Super(object): 
    ...  def __init__(self): 
    ...   print "In super init" 
    ...   super(Sub, self).__init__() 
    >>> class Sub(Super): 
    ...  def __init__(self): 
    ...   print "In sub init" 
    ...   super(Sub, self).__init__() 
    

    하는 것으로 Supersuper(Sub, self)를 호출

효과는 실행 경로가 더 분명 할 수도 있습니다 코드와 동일합니다. 수퍼 클래스 메서드를 호출하려고 시도하지만 수퍼 클래스 메서드 Sub을 호출하려고합니다. Sub의 수퍼 클래스는 Super이므로 Super은 자체 메소드를 다시 호출합니다.

편집 : 그냥 당신이 제기 된 이름 조회 문제를 명확히하기 위해, 여기에 동일한 결과가 다른 약간 다른 예입니다 :

>>> class Super(object): 
...  def __init__(self): 
...   print "In super init" 
...   super(someClass, self).__init__() 
>>> class Sub(Super): 
...  def __init__(self): 
...   print "In sub init" 
...   super(Sub, self).__init__() 
>>> someClass = Sub 

이 분명히 있음을 확인해야을 super의 클래스 인수 (첫 번째 인수 , 여기 someClass) 어떤 식 으로든 특별하지 않습니다. 이것은 보통의 시간에 일반적인 방법으로, 즉 super 호출이 실행될 때 값이 조회되는 일반 이름입니다. 이 예제에 표시된 것처럼 변수를 정의 할 때 변수가 존재하지 않아도됩니다. 그 값은 으로 전화 할 때 조회됩니다.

+0

오. 이해하기 어려웠습니다. 그러나 나는 그것이 나에게있는 것처럼 보인다! 내가 이해하지 못했던 핵심은 '슈퍼 (MyClass, 자기)'가 언제나 'OrigMyClass'를 말하는 것이 아니라 어떤 시점 (장식 작업 후)에서 'DecMyClass'를 참조하기 시작한 이유였습니다. 그리고 결국'__init__'는 모듈의 범위에서'MyClass'를 찾을 때까지 lixical 스코프를 찾았습니다.이 클래스는'DecMyClass'를 참조합니다. 그 이해하기 전에 나는'__init__'가'MyClass' 내부에 존재한다고 생각했기 때문에 항상 초기 클래스'OrigMyClass'에 바인딩되어야합니다. 이제 모든 것이 명확합니다! 고맙습니다! – ovgolovin

+0

@ovgolovin : 그렇습니다. 그러나 이것이 언제나 * 일어나는 것과 같은 것은 가치가 없습니다. 함수의 모든 이름에 대한 참조는 항상 (이름을 할당하지 않는 한)이 방법으로 조회됩니다. 그것은 '슈퍼'에만 국한된 것이 아닙니다. – BrenBarn

+0

@ovgolovin : 이것을 설명하기위한 또 다른 예제를 추가했습니다. – BrenBarn

관련 문제