programing

여러 상속을 가진 부모 클래스 __init__를 호출하는 올바른 방법은 무엇입니까?

sourcejob 2022. 9. 25. 00:19
반응형

여러 상속을 가진 부모 클래스 __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__:

  1. (구식) (구식)ParentClass.__init__(self)
  2. (외관 스타일)super(DerivedClass, self).__init__()

어느, 다', '다', '다', '다', '다', '다')의 경우는,A ★★★★★★★★★★★★★★★★★」B)가 같은 규칙을 따르지 않으면 코드가 올바르게 작동하지 않습니다(일부 코드가 누락되거나 여러 번 호출될 수 있습니다).

럼럼어 떻떻 ?? ???만 하면 , 'A ★★★★★★★★★★★★★★★★★」B서드파티 도서관의 자료인데, 그 다음엔요?모든 부모 클래스 컨스트럭터를 호출할 수 있는 접근법이 있습니까(올바른 순서로 한 번만 호출할 수 있습니까?

편집: 무슨 뜻인지 보려면 다음 작업을 수행합니다.

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인it 2로 하다.경우: ★★★★★★★★★★★★★★★★★★★★:

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초기화하다 내가하지 않는 한(는) 것.A ★★★★★★★★★★★★★★★★★」B 있는 을 할 수 C를 참조해 주세요.

질문에 대한 답변은 매우 중요한 한 가지 측면에 따라 달라집니다.기본 클래스는 여러 상속용으로 설계되어 있습니까?

다음 3가지 시나리오가 있습니다.

  1. 기본 클래스는 관련이 없는 독립 실행형 클래스입니다.

    기본 클래스가 독립적으로 작동할 수 있는 개별 엔티티이고 서로 알지 못하는 경우에는 여러 상속을 위해 설계되지 않습니다.예:

    class Foo:
        def __init__(self):
            self.foo = 'foo'
    
    class Bar:
        def __init__(self, bar):
            self.bar = bar
    

    중요:어느 쪽도Foo 않다Bar ®super().__init__()이것이 당신의 코드가 제대로 작동하지 않은 이유입니다.다이아몬드 상속이 python에서 작동하는 방식 때문에 기본 클래스가 클래스는 를 호출하지 않아야 합니다.이미 알고 있듯이 그렇게 하면 여러 상속이 중단됩니다. 왜냐하면 다른 클래스의 상속을 호출하게 되기 때문입니다.__init__object.__init__()(면책자: subclasses에서의 회피는 제가 개인적으로 추천하는 사항이며 python 커뮤니티에서 합의된 내용은 아닙니다. 어떤 사람들은 모든 클래스에서 사용하는 것을 선호합니다.클래스가 예상대로 동작하지 않으면 항상 어댑터를 쓸 수 있다고 주장합니다.)

    또, 이 수업을 이기도 합니다.object 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 가 없어요__init__정의하지 않다__init__는, 「부탁」을하는 것과 같은 .super().__init__() inherits directly directly directly directly directly directly directly directly directly directly directly directly directly directly directly directly directly 에서 직접 계승하는 경우object다음과 같이 빈 컨스트럭터를 추가해야 합니다.

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

    어쨌든 이 상황에서는 각 부모 컨스트럭터를 수동으로 호출해야 합니다.여기에는 다음 두 가지 방법이 있습니다.

    • super

      class FooBar(Foo, Bar):
          def __init__(self, bar='bar'):
              Foo.__init__(self)  # explicit calls without super
              Bar.__init__(self, bar)
      
    • ★★★★★★★★★★★★★★★★ super

      class FooBar(Foo, Bar):
          def __init__(self, bar='bar'):
              super().__init__()  # this calls all constructors up to Foo
              super(Foo, self).__init__(bar)  # this calls all constructors after Foo up
                                              # to Bar
      

    이 두 가지 방법에는 각각 장점과 단점이 있습니다.「 」를 사용하고 super클래스는 의존성 주입을 지원합니다.반면에, 실수를 하는 것은 더 쉽다.예를 들어, 의 순서를 변경하는 경우Foo ★★★★★★★★★★★★★★★★★」Bar 것)class FooBar(Bar, Foo)을(를) .super일치시킬 수 있습니다.super이것에 대해서는 걱정할 필요가 없습니다.이치

  2. 수업 중 하나는 믹스인입니다.

    믹스인은 여러 상속과 함께 사용하도록 설계된 클래스입니다.즉, mixin이 자동으로 두 번째 컨스트럭터를 호출하기 때문에 양쪽 부모 컨스트럭터를 수동으로 호출할 필요가 없습니다.이번에는 컨스트럭터를 1개만 호출하면 되기 때문에super부모 클래스의 이름을 하드 코드하지 않아도 됩니다.

    예:

    class FooMixin:
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)  # forwards all unused arguments
            self.foo = 'foo'
    
    class Bar:
        def __init__(self, bar):
            self.bar = bar
    
    class FooBar(FooMixin, Bar):
        def __init__(self, bar='bar'):
            super().__init__(bar)  # a single call is enough to invoke
                                   # all parent constructors
    
            # NOTE: `FooMixin.__init__(self, bar)` would also work, but isn't
            # recommended because we don't want to hard-code the parent class.
    

    여기서 중요한 세부 사항은 다음과 같습니다.

    • 이 「」를 합니다.super().__init__()수신된 인수를 통과합니다.
    • 서브클래스는 먼저 mixin에서 상속됩니다.class FooBar(FooMixin, Bar)기본 클래스의 순서가 올바르지 않은 경우, 믹스인의 컨스트럭터는 호출되지 않습니다.
  3. 모든 기본 클래스는 공동 상속을 위해 설계되었습니다.

    공동 상속을 위해 설계된 클래스는 믹스인과 매우 유사합니다.사용되지 않는 모든 인수를 다음 클래스로 전달합니다.처럼 전화만 super().__init__()모든 부모 컨스트럭터는 체인 콜이 됩니다.

    예:

    class CoopFoo:
        def __init__(self, **kwargs):
            super().__init__(**kwargs)  # forwards all unused arguments
            self.foo = 'foo'
    
    class CoopBar:
        def __init__(self, bar, **kwargs):
            super().__init__(**kwargs)  # forwards all unused arguments
            self.bar = bar
    
    class CoopFooBar(CoopFoo, CoopBar):
        def __init__(self, bar='bar'):
            super().__init__(bar=bar)  # pass all arguments on as keyword
                                       # arguments to avoid problems with
                                       # positional arguments and the order
                                       # of the parent classes
    

    이 경우 부모 클래스의 순서는 중요하지 않습니다.우리가 물려받는게 좋을 것 같아CoopBar먼저, 그리고 코드는 여전히 동일하게 작동합니다.그러나 이는 모든 인수가 키워드 인수로 전달되기 때문에 해당됩니다.를 사용하면 인수만 입니다.

    이것도 앞에서 설명한 규칙에 대한 예외입니다. 다.CoopFoo ★★★★★★★★★★★★★★★★★」CoopBar을 받다object 「」라고 하는 콜은 행하고 있습니다.super().__init__()을 사용하다

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

생성자는 클래스의 공용 인터페이스의 일부입니다.클래스가 믹스인 또는 공동 상속용으로 설계된 경우 해당 내용을 문서화해야 합니다.문서에 이러한 내용이 없는 경우 클래스는 공동 다중 상속용으로 설계되지 않았다고 가정해도 무방합니다.

어느 쪽이든 문제 없습니다.를 사용한 super()이치노

콜에서는, 「(직접 통화)」에서는,C.__init__ 다 할 수 A.__init__ ★★★★★★★★★★★★★★★★★」B.__init__.

「」를 사용하고 super()는 공동 다중 상속을 위해 설계되어야 합니다.여기서 , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,C ®super를 호출합니다.Asuper를 호출하다B의 코드입니다.에서 실행할 수 있는 기능의 자세한 내용은, http://rhettinger.wordpress.com/2011/05/26/super-considered-super 를 참조해 주세요.super.

[나중에 편집한 답변 질문]

따라서 (A 및 B)로부터 상속받는 클래스의 초기화를 알고/제어하지 않으면 (C) 작성 중인 클래스에 대해 안전한 선택을 할 수 없는 것 같습니다.

클래스가 "아까부터"에 되어 이 줍니다.A ★★★★★★★★★★★★★★★★★」B「비협력 클래스의 통합 방법」의 섹션에는, 해결된 예가 있습니다.

FlyingCar를 얻기 위해 자동차와 비행기 클래스를 쉽게 구성할 수 있도록 여러 번 상속이 더 쉬웠으면 좋겠지만, 실제로는 별도로 설계된 컴포넌트가 서로 맞물리기 전에 어댑터나 래퍼가 필요한 경우가 많습니다.

또 다른 생각은, 복수의 상속을 사용해 기능을 구성하는 것에 만족하지 않는 경우는, 어느 경우에 어떤 메서드가 호출되는지를 완전하게 제어하기 위해서, 구성을 사용할 수 있다는 것입니다.

및 의 소스 코드를 제어할 수 있는 경우 접근법("new style" 또는 "old style") 중 하나가 작동합니다.그렇지 않으면 어댑터 클래스를 사용해야 할 수 있습니다.

액세스 가능한 소스 코드: "새로운 스타일"의 올바른 사용

class A(object):
    def __init__(self):
        print("-> A")
        super(A, self).__init__()
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        super(B, self).__init__()
        print("<- B")

class C(A, B):
    def __init__(self):
        print("-> C")
        # Use super here, instead of explicit calls to __init__
        super(C, self).__init__()
        print("<- C")
>>> C()
-> C
-> A
-> B
<- B
<- A
<- C

여기서 Method Resolution Order(MRO; 메서드 해결 순서)는 다음을 나타냅니다.

  • C(A, B)A 번째, 두 번째, 두 번째, 세 번째B는 MRO입니다C -> A -> B -> object.
  • super(A, self).__init__()는 MRO에서 시작된 됩니다.C.__init__로로 합니다.B.__init__.
  • super(B, self).__init__()는 MRO에서 시작된 됩니다.C.__init__로로 합니다.object.__init__.

이 케이스는 여러 상속을 위해 설계되었다고 할 수 있습니다.

액세스 가능한 소스 코드: "이전 스타일"의 올바른 사용

class A(object):
    def __init__(self):
        print("-> A")
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        # Don't use super here.
        print("<- B")

class C(A, B):
    def __init__(self):
        print("-> C")
        A.__init__(self)
        B.__init__(self)
        print("<- C")
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

되지 .MRO는 MRO이기 때문입니다.A.__init__ ★★★★★★★★★★★★★★★★★」B.__init__을 사용하다 class C(B, A):네, 네, 네.

이 경우 이전 스타일과 같이 새로운 스타일의 다중 상속을 위해 "설계"된 것은 아니지만 다중 상속은 여전히 가능합니다.


그럼 에 ,, 약, 에, 에, 에, now, now.A ★★★★★★★★★★★★★★★★★」B서드파티 라이브러리에서 가져온 것입니다.즉, 및 의 소스 코드를 제어할 수 없습니다.간단한 답변:필요한 기능을 구현하는 어댑터 클래스를 설계해야 합니다.super콜 후 빈 클래스를 사용하여 MRO를 정의합니다(에 관한 Raymond Hettinger 기사, 특히 "비협력 클래스를 통합하는 방법" 섹션을 참조하십시오).

부모: ★★★★★★★★★★★★★★★★★★★★★★★★★★:A does does does does 。superB

class A(object):
    def __init__(self):
        print("-> A")
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        super(B, self).__init__()
        print("<- B")

class Adapter(object):
    def __init__(self):
        print("-> C")
        A.__init__(self)
        super(Adapter, self).__init__()
        print("<- C")

class C(Adapter, B):
    pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

★★Adapter를 실장하다super 해서C는 MRO를 할 수 MRO가 사용되었을 때 됩니다.MRO를 사용하다super(Adapter, self).__init__()행됩니니다다

만약 그 반대라면요?

부모: ★★★★★★★★★★★★★★★★★★★★★★★★★★:A를 실장하다superB 않다

class A(object):
    def __init__(self):
        print("-> A")
        super(A, self).__init__()
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        print("<- B")

class Adapter(object):
    def __init__(self):
        print("-> C")
        super(Adapter, self).__init__()
        B.__init__(self)
        print("<- C")

class C(Adapter, A):
    pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

만, 있습니다.Adapter.__init__super먼저 콜하고 다음으로 명시적인 콜입니다.서드파티제 부모를 사용하는 경우마다 고유한 어댑터 클래스가 필요합니다.

내가하지 않는 한(는) 것.A ★★★★★★★★★★★★★★★★★」B 있는 을 할 수 C를 참조해 주세요.

소스코드를 제어하지 않는 경우는 처리할 수 있지만A ★★★★★★★★★★★★★★★★★」B어댑터 클래스를 사용함으로써 부모 클래스의 초기값이 어떻게 구현되는지 알아야 합니다.super ,

처럼, 「Direct 」는 「 Call 」(입니다.A.__init__ ★★★★★★★★★★★★★★★★★」B.__init__정상적으로 동작하며, 코드를 읽을 수 있습니다.

,, 음, 음, 음, 음, 다, 다, 다, ance, ance, ance, however, however, however, however, however, however, however, however, however, however, however, however, however, 의 상속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")

이 문서에서는 공동 다중 상속에 대해 설명합니다.

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

한 방법을 하고 있습니다.mro()메서드 해결 순서를 나타냅니다.에서는 " "를 합니다.superA , . . . . . . . .superMRO를 사용하다 순서는 서음음 in 음 in 음 in 음 in 음 the the in the 。B 입니다.B【init】처음 【init】

다음은 공식 python 사이트의 기술 기사입니다.

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

개 사용하고 경우, 클래스를 .__init__기본 클래스의 프로그래밍 방법에 관계없이 실제로 작동하는 메서드(또는 다른 메서드)입니다.

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

가 '클래스'를 사용하여 수 super또는 베이스 클래스에 직접 콜을 하는 경우는, 클래스의 「퍼블릭 인터페이스」의 일부인 속성이므로, 그 내용을 문서화해 둘 필요가 있습니다.라이브러리 작성자가 예상한 대로 서드파티 라이브러리를 사용하고 있고 라이브러리에 적절한 문서가 있는 경우, 일반적으로 특정 항목을 분류하기 위해 필요한 작업을 알려줍니다.그렇지 않은 경우 서브클래스로 분류하는 클래스의 소스 코드를 확인하고 base-class-incallation 규칙을 확인해야 합니다.라이브러리 작성자가 예상하지 못한 방법으로 하나 이상의 서드파티 라이브러리에서 여러 클래스를 결합하는 경우 슈퍼클래스 메서드를 일관되게 호출할 수 없을 수 있습니다.클래스 A가 다음을 사용하는 계층의 일부인 경우super클래스 B는 슈퍼를 사용하지 않는 계층의 일부이므로 어느 옵션도 동작하지 않습니다.각 케이스에 적합한 전략을 생각해내야 합니다.

작은 유틸리티 라이브러리인 super를 추가했습니다.이러한 종류의 시나리오는 간단하게 처리됩니다.다음과 같이 동작합니다.

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")
        supers(self).__init__()
        print("Leaving C")

C 작성 시 출력:

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

1. 우선 MRO 체인을 가지고 있다고 가정합니다.

2. 가장 낮은 수준의 서브클래스 init 메서드에서 super() 메서드를 사용하는 클래스는 대응하는 체인 위치로 점프합니다.super() 메서드를 사용하지 않는 클래스는 대응하는 체인 위치에서 점프합니다.

여기에 오류가 있으면 수정해 주세요!

다음은 super()를 사용하여 python 3에서 다중 상속을 구현한 방법입니다.

class A:
  def __init__(self,a,b,**kwargs):
      print("Class A initiallised")
      self.a=a
      self.b=b
      super().__init__(**kwargs)
      print("Class A initiallisation done")

  def __str__(self):
      return f"{self.a} and {self.b}"

  def display_a(self):
      return f"{self.a} and {self.b}"

class C:
   def __init__(self,c,d,**kwargs):
      print("Class C initiallised")
      self.c=c
      self.d=d
      super().__init__(**kwargs)
      print("class c initiallisation done")

   def __str__(self):
      return f"{self.c} and {self.d}"

   def display_c(self):
       return f"{self.c} and {self.d}"


class D(A,C):
   def __init__(self,e,**kwargs):
       print("Class D initiallised")
       super().__init__(**kwargs)
       self.e=e
       print("Class D initiallisation done")

   def __str__(self):
      return f"{self.e} is e,{self.b} is b,{self.a} is a,{self.d} is d,{self.c} is c"

if __name__=="__main__":
   d=D(a=12,b=13,c=14,d=15,e=16)
   print(d)
   d.display_c()
   d.display_a()

Python 상속에서 슈퍼 메서드를 구현하여 필요한 솔루션을 구현한 방법은 다음과 같습니다.

class A:
    def __init__(self):
        print("from A")

class B:
    def __init__(self):
        print("from B")
class C(A,B):
    def __init__(self):
          A.__init__(self)
          B.__init__(self)
          print ("from C")
c = C()

MRO 규칙에 따라 init이 호출됩니다.

언급URL : https://stackoverflow.com/questions/9575409/calling-parent-class-init-with-multiple-inheritance-whats-the-right-way

반응형