복붙노트

[PYTHON] 다중 상속을 가진 부모 클래스 __init__을 호출하는 것이 옳은 방법일까요?

PYTHON

다중 상속을 가진 부모 클래스 __init__을 호출하는 것이 옳은 방법일까요?

다중 상속 시나리오가 있다고 가정 해보십시오.

class A(object):
    # code for A here

class B(object):
    # code for B here

class C(A, B):
    def __init__(self):
        # What's the right code to write here to ensure 
        # A.__init__ and B.__init__ get called?

C의 __init__ 작성에는 두 가지 일반적인 접근 방식이 있습니다.

그러나 두 경우 모두 부모 클래스 (A와 B)가 동일한 규칙을 따르지 않으면 코드가 제대로 작동하지 않습니다 (일부는 누락되거나 여러 번 호출 될 수 있음).

그럼 올바른 길은 또 뭐야? "단지 일관성 있고, 하나 또는 다른 것을 따르라."라고 말하기는 쉽지만, A 나 B가 제 3 자 라이브러리에서왔다면, 그 때 무엇을해야할까요? 모든 부모 클래스 생성자가 호출되는 것을 보장 할 수있는 접근법이 있습니까? (올바른 순서로 한 번만 호출됩니다.)

편집 : 내가 무슨 뜻인지 알기 위해, 내가 할 경우 :

class A(object):
    def __init__(self):
        print("Entering A")
        super(A, self).__init__()
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

class C(A, B):
    def __init__(self):
        print("Entering C")
        A.__init__(self)
        B.__init__(self)
        print("Leaving C")

그 때 나는 얻는다 :

Entering C
Entering A
Entering B
Leaving B
Leaving A
Entering B
Leaving B
Leaving C

B의 init은 두 번 호출됩니다. 만약 내가한다면:

class A(object):
    def __init__(self):
        print("Entering A")
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

class C(A, B):
    def __init__(self):
        print("Entering C")
        super(C, self).__init__()
        print("Leaving C")

그 때 나는 얻는다 :

Entering C
Entering A
Leaving A
Leaving C

B의 init은 결코 호출되지 않습니다. 따라서 내가 (A와 B)로부터 상속받은 클래스의 초기화를 제어하지 않는다면, 나는 쓰고있는 클래스에 대해 안전한 선택을 할 수 없다.

해결법

  1. ==============================

    1.두 가지 방법 모두 잘 작동합니다. super ()를 사용하는 접근 방식은 하위 클래스에 더 큰 유연성을 제공합니다.

    두 가지 방법 모두 잘 작동합니다. super ()를 사용하는 접근 방식은 하위 클래스에 더 큰 유연성을 제공합니다.

    직접 호출 방식에서 C .__ init__은 A .__ init__과 B .__ init__을 모두 호출 할 수 있습니다.

    super ()를 사용할 때, 클래스는 C가 super를 호출하는 협력적인 다중 상속을 위해 설계되어야합니다.이 상속은 B의 코드를 호출하는 super를 호출 할 A의 코드를 호출합니다. 슈퍼와 함께 할 수있는 일에 대한 자세한 내용은 http://rhettinger.wordpress.com/2011/05/26/super-considered-super를 참조하십시오.

    [나중에 편집 된 응답 질문]

    참조 된 기사에서는 A와 B 주위에 래퍼 클래스를 추가하여이 상황을 처리하는 방법을 보여줍니다. "비협조적 클래스를 통합하는 방법"절에 해결 된 예제가 있습니다.

    Car and Airplane 클래스를 사용하여 FlyingCar를 쉽게 생성 할 수 있도록 다중 상속이 더 쉬웠 으면 좋겠지 만 실제로 별도로 설계된 구성 요소는 종종 어댑터 또는 래퍼가 필요하기 때문에 원활하게 연결될 수 있습니다 :-)

    또 다른 생각 : 다중 상속을 사용하여 기능을 구성하는 데 불만이있는 경우 컴포지션을 사용하여 어떤 메소드가 어떤 경우에 호출되는지 완벽하게 제어 할 수 있습니다.

  2. ==============================

    2.귀하의 질문에 대한 대답은 하나의 매우 중요한 측면에 달렸습니다 : 기본 클래스가 다중 상속을 위해 설계 되었습니까?

    귀하의 질문에 대한 대답은 하나의 매우 중요한 측면에 달렸습니다 : 기본 클래스가 다중 상속을 위해 설계 되었습니까?

    세 가지 시나리오가 있습니다.

    .

        class Base(object):
            def __init__(self):
                pass
    

    결론 : 올바른 구현은 상속받는 클래스에 따라 다릅니다.

    생성자는 클래스의 공용 인터페이스의 일부입니다. 클래스가 mixin 또는 협력 상속 용으로 설계된 경우 문서화해야합니다. 문서에 일종의 언급이 없으면 클래스가 다중 상속을 위해 설계되지 않았으며 각 상위 클래스의 생성자를 명시 적으로 (수퍼 제외) 호출해야한다고 가정하는 것이 안전합니다.

  3. ==============================

    3.이 기사에서는 협동 적 다중 상속에 대해 설명합니다.

    이 기사에서는 협동 적 다중 상속에 대해 설명합니다.

    http://www.artima.com/weblogs/viewpost.jsp?thread=281127

    메서드 확인 순서를 보여주는 유용한 메서드 mro ()에 대해 설명합니다. 두 번째 예에서 A에서 super를 호출하면 super 호출이 MRO에서 계속됩니다. 순서의 다음 클래스는 B이므로 B의 init가 처음 호출되는 이유입니다.

    다음은 공식 파이썬 사이트의보다 전문적인 기사입니다.

    http://www.python.org/download/releases/2.3/mro/

  4. ==============================

    4.제 3 자 라이브러리의 하위 클래스를 여러 개 곱한 경우, 아니요, 기본 클래스가 프로그래밍 된 방식에 관계없이 실제로 작동하는 기본 클래스 __init__ 메소드 (또는 다른 메소드)를 호출하는 시각 접근 방식이 없습니다.

    제 3 자 라이브러리의 하위 클래스를 여러 개 곱한 경우, 아니요, 기본 클래스가 프로그래밍 된 방식에 관계없이 실제로 작동하는 기본 클래스 __init__ 메소드 (또는 다른 메소드)를 호출하는 시각 접근 방식이 없습니다.

    super는 클래스 작성자가 알 필요가없는 복잡한 다중 상속 트리의 일부로 메소드를 협업 적으로 구현하도록 설계된 클래스를 작성할 수 있습니다. 그러나 super를 사용할 수도 있고 사용하지 않을 수도있는 임의의 클래스에서 올바르게 상속하는 데 사용할 방법이 없습니다.

    기본적으로 클래스가 슈퍼 클래스를 사용하여 하위 클래스로 설계 되었든 기본 클래스에 대한 직접 호출과 함께 클래스가 "공용 인터페이스"클래스의 일부인 속성인지 여부는 문서화되어야합니다. 라이브러리 작성자가 예상 한 방식으로 타사 라이브러리를 사용하고 있고 라이브러리에 적절한 설명서가있는 경우 일반적으로 특정 항목을 하위 클래스로 분류하기 위해 수행해야하는 작업을 알려줍니다. 그렇지 않다면 하위 클래스로 분류 할 클래스의 소스 코드를 살펴보고 기본 클래스 호출 규칙이 무엇인지 확인해야합니다. 라이브러리 작성자가 예상하지 못한 방식으로 하나 이상의 타사 라이브러리에서 여러 클래스를 결합하는 경우 수퍼 클래스 메소드를 일관되게 호출 할 수 없을 수도 있습니다. 클래스 A가 super를 사용하는 계층 구조의 일부이고 클래스 B가 super를 사용하지 않는 계층 구조의 일부인 경우, 어느 옵션도 작동하지 않을 것입니다. 특정 사례별로 효과가있는 전략을 찾아야합니다.

  5. ==============================

    5.Raymond가 대답했듯이, .__ init__ 및 B .__ init__에 대한 직접 호출은 잘 작동하고 코드는 읽기 쉽습니다.

    Raymond가 대답했듯이, .__ init__ 및 B .__ init__에 대한 직접 호출은 잘 작동하고 코드는 읽기 쉽습니다.

    그러나 C와 해당 클래스 간의 상속 링크는 사용하지 않습니다. 이 링크를 사용하면 일관성이 향상되고 궁극적 인 리팩토링을보다 쉽고 오류를 발생시키지 않게 할 수 있습니다. 이를 수행하는 방법의 예 :

    class C(A, B):
        def __init__(self):
            print("entering c")
            for base_class in C.__bases__:  # (A, B)
                 base_class.__init__(self)
            print("leaving c")
    
  6. from https://stackoverflow.com/questions/9575409/calling-parent-class-init-with-multiple-inheritance-whats-the-right-way by cc-by-sa and MIT license