파이썬의 super ()는 다중 상속에서 어떻게 작동합니까?


887

나는 파이썬 객체 지향 프로그래밍에서 거의 새로 super()왔으며 특히 다중 상속 과 관련하여 함수 (새로운 스타일 클래스)를 이해하는 데 어려움이 있습니다.

예를 들어 다음과 같은 것이 있다면 :

class First(object):
    def __init__(self):
        print "first"

class Second(object):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__()
        print "that's it"

내가 얻지 못하는 것은 Third()클래스가 두 생성자 메서드를 상속합니까? 그렇다면 어떤 것이 super ()로 실행될 것이며 왜?

다른 것을 실행하려면 어떻게해야합니까? 파이썬 메소드 분석 순서 ( MRO ) 와 관련이 있다는 것을 알고 있습니다 .


실제로 다중 상속이 유일한 사용 사례 super()입니다. 쓸모없는 오버 헤드 인 선형 상속을 사용하는 클래스와 함께 사용하지 않는 것이 좋습니다.
Bachsau

9
@Bachsau는 작은 오버 헤드이지만 super ()는 더 비판적이며 시간이 지남에 따라 코드를 리팩토링하고 변경할 수 있다는 점에서 기술적으로 정확합니다. 실제로 명명 된 클래스 특정 메소드가 필요하지 않은 경우 super ()를 사용하십시오.
Paul Whipp

2
또 다른 문제 super()는 모든 서브 클래스가 그것을 사용하도록 강요하지만을 사용하지 않을 때 서브 클래스를 사용하는 super()모든 사람이 스스로 결정할 수 있다는 것입니다. 그것을 사용하는 개발자가 그것을 사용 super()하거나 모르는 경우, 추적하기 매우 어려운 mro 문제가 발생할 수 있습니다.
Bachsau

나는 여기에서 실제로 각 대답이 어떤 식 으로든 혼란 스럽습니다. 실제로 대신 여기 를 참조 하십시오 .
matanster

답변:


709

자세한 내용은 Guido 자신의 블로그 게시물 Method Resolution Order (이전에 시도한 두 가지 시도 포함) 에서 합리적으로 자세히 설명되어 있습니다.

귀하의 예에서는 Third()을 호출 First.__init__합니다. 파이썬은 왼쪽에서 오른쪽으로 나열된 클래스의 부모에서 각 속성을 찾습니다. 이 경우 찾고 있습니다 __init__. 따라서 정의하면

class Third(First, Second):
    ...

파이썬은보고 시작 First하면, 그리고 First속성이없는, 그때는 볼 것이다 Second.

상속이 경로를 넘을 때 (예 : First에서 상속 된 경우 Second) 이 상황이 더 복잡해집니다 . 자세한 내용은 위의 링크를 읽으십시오. 그러나 간단히 말해서 Python은 자식 클래스 자체부터 시작하여 상속 목록에 각 클래스가 나타나는 순서를 유지하려고 시도합니다.

예를 들어 다음과 같은 경우

class First(object):
    def __init__(self):
        print "first"

class Second(First):
    def __init__(self):
        print "second"

class Third(First):
    def __init__(self):
        print "third"

class Fourth(Second, Third):
    def __init__(self):
        super(Fourth, self).__init__()
        print "that's it"

MRO는 [Fourth, Second, Third, First].

그런데 파이썬이 일관된 메소드 해결 순서를 찾지 못하면 사용자를 놀라게 할 수있는 동작으로 돌아 가지 않고 예외가 발생합니다.

모호한 MRO의 예를 추가하도록 편집되었습니다.

class First(object):
    def __init__(self):
        print "first"

class Second(First):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        print "third"

한다 Third의 MRO는 수 [First, Second]또는 [Second, First]? 명백한 기대는 없으며 Python은 오류를 발생시킵니다.

TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution order (MRO) for bases Second, First

편집 : 위의 예제에는 super()호출이 부족하다고 주장하는 사람들이 여러 명 있습니다. 예제 의 요점은 MRO 구성 방법을 보여주는 것입니다. 그들은되어 있지 "처음 \ nsecond \ 셋째"또는 무엇이든을 인쇄하기위한 것. 물론 예제를 가지고 놀거나, super()호출을 추가 하고, 어떤 일이 발생하는지 확인하고, 파이썬의 상속 모델에 대해 더 깊이 이해할 수 있습니다. 그러나 여기서의 목표는 간단하게 유지하고 MRO가 어떻게 구축되는지 보여주는 것입니다. 그리고 내가 설명한대로 작성되었습니다.

>>> Fourth.__mro__
(<class '__main__.Fourth'>,
 <class '__main__.Second'>, <class '__main__.Third'>,
 <class '__main__.First'>,
 <type 'object'>)

12
첫 번째, 두 번째 및 세 번째 [ pastebin.com/ezTyZ5Wa ] 에서 super ()를 호출하기 시작하면 더욱 흥미롭고 혼란스러워 집니다.
gatoatigrado

52
첫 번째 수업에서 수퍼 콜이 결여 된 것이이 답변에서 큰 문제라고 생각합니다. 그 질문에 대한 중요한 비판적 이해가 어떻게 / 왜 논의되지 않고.
Sam Hartman

3
이 대답은 단순히 잘못되었습니다. 부모에게 super () 호출이 없으면 아무 일도 일어나지 않습니다. @lifeless의 대답은 정답입니다.
Cerin

8
@Cerin이 예제의 핵심은 MRO 구성 방법을 보여주는 것입니다. 이 예는 "first \ nsecond \ third"등을 인쇄하기위한 것이 아닙니다. 그리고 MRO는 참으로 올바른 : 네 번째 .__ mro__ == (<클래스의 주요 .Fourth '>, <클래스의 주요 .Second'>, <클래스의 주요 .Third '>, <클래스의 주요 좁은 방'>, < 'object'유형>)
rbp

2
내가 알 수있는 한,이 답변에는 OP의 질문 중 하나가 없습니다. "다른 질문을 실행하려면 어떻게해야합니까?"입니다. 이 질문에 대한 답을보고 싶습니다. 기본 클래스의 이름을 명시 적으로 지정해야합니까?
레이

251

귀하의 코드와 다른 답변은 모두 버그가 있습니다. super()협동 서브 클래 싱이 작동하는 데 필요한 처음 두 클래스 의 호출 이 누락되었습니다 .

다음은 고정 버전의 코드입니다.

class First(object):
    def __init__(self):
        super(First, self).__init__()
        print("first")

class Second(object):
    def __init__(self):
        super(Second, self).__init__()
        print("second")

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__()
        print("third")

super()호출은 각 단계에서 MRO에서 다음 메소드를 찾으므로 First와 Second도 메소드를 가져야하며 그렇지 않으면의 끝에서 실행이 중지됩니다 Second.__init__().

이것이 내가 얻는 것입니다 :

>>> Third()
second
first
third

90
이 클래스들이 다른 파라미터를 필요로한다면 어떻게해야합니까?
calfzhou

2
"협동 조합 서브 클래 싱"
Quant Metropolis

6
이런 식 으로 BOTH 기본 클래스 의 init 메소드가 실행되는 반면, 원래 예제 는 MRO에서 처음으로 발견 된 init 만 호출합니다 . ) 나는 (당신은 알고있다 '노골적인 더 나은 암시보다') 그 용어는 "협력 서브 클래스"에 의해 암시하지만, 설명이 유용했을 것 같아요
퀀트 대도시

1
예, super를 통해 호출되는 메소드에 다른 매개 변수를 전달하는 경우 object ()를 향한 MRO로 올라가는 해당 메소드의 모든 구현에는 호환 가능한 서명이 있어야합니다. 키워드 매개 변수를 통해 달성 할 수 있습니다. 메소드가 사용하는 것보다 많은 매개 변수를 승인하고 추가 매개 변수를 무시하십시오. 일반적 으로이 작업을 수행하는 것은 추악한 것으로 간주되며 대부분의 경우 새 메소드를 추가하는 것이 더 좋지만 init 는 특수 메소드 이름으로 고유하지만 (사용자 정의 매개 변수는) 거의 없습니다.
생명이없는

15
다중 상속 디자인 은 실제로 파이썬에서 나쁩니다. 기본 클래스는 거의 (가) 파생됩니다 파생 된 많은 다른 기본 클래스 그것을 유도하는 것, 어떻게 누구인지해야하고, 어떤 순서로 ... 다른 super하나 (때문에 매개 변수 불일치) 실행하는 데 실패하거나 호출하지 않습니다 것입니다 베이스 중 몇 개 ( super링크를 끊는베이스 중 하나에 쓰지 않았기 때문에 )!
Nawaz

186

파이썬에서 다중 상속 계층 구조에서 super ()를 사용하는 방법에 대해 읽었을 때 즉시 그것을 얻지 못했기 때문에 생명력 이 조금씩 을 정교화 하고 싶었습니다 .

이해해야 할 것은 완전한 상속 계층의 맥락에서 사용 MRO (Method Resolution Ordering) 알고리즘 따라 다음 방법 을 super(MyClass, self).__init__()제공 한다는 것입니다 . __init__

이 마지막 부분은 이해하는 데 중요합니다. 예제를 다시 고려해 봅시다.

#!/usr/bin/env python2

class First(object):
  def __init__(self):
    print "First(): entering"
    super(First, self).__init__()
    print "First(): exiting"

class Second(object):
  def __init__(self):
    print "Second(): entering"
    super(Second, self).__init__()
    print "Second(): exiting"

class Third(First, Second):
  def __init__(self):
    print "Third(): entering"
    super(Third, self).__init__()
    print "Third(): exiting"

Guido van Rossum의 Method Resolution Order대한이 기사에 따르면 , 해결 순서 __init__는 "깊이 우선 왼쪽에서 오른쪽 순회"를 사용하여 계산됩니다 (Python 2.3 이전).

Third --> First --> object --> Second --> object

마지막 것을 제외하고 모든 중복을 제거하면 다음과 같이 나타납니다.

Third --> First --> Second --> object

Third예를 들어 클래스 의 인스턴스를 인스턴스화 할 때 발생하는 상황을 따르십시오 ( 예 :) x = Third().

  1. MRO에 따르면 Third.__init__실행됩니다.
    • 인쇄물 Third(): entering
    • 그런 다음 super(Third, self).__init__()실행되고 MRO First.__init__가 호출됩니다.
  2. First.__init__ 실행합니다.
    • 인쇄물 First(): entering
    • 그런 다음 super(First, self).__init__()실행되고 MRO Second.__init__가 호출됩니다.
  3. Second.__init__ 실행합니다.
    • 인쇄물 Second(): entering
    • 그런 다음 super(Second, self).__init__()실행되고 MRO object.__init__가 호출됩니다.
  4. object.__init__ 실행 (코드에 인쇄 문장이 없음)
  5. 실행은 다시 돌아가서 Second.__init__인쇄Second(): exiting
  6. 실행은 다시 돌아가서 First.__init__인쇄First(): exiting
  7. 실행은 다시 돌아가서 Third.__init__인쇄Third(): exiting

여기에 Third ()를 인스턴스화하면 다음과 같은 결과가 나타납니다.

Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting

MRO 알고리즘은 복잡한 경우에는 잘 작동하도록 Python 2.3부터 개선되었지만 "깊이 우선 왼쪽에서 오른쪽 순회"+ "중복 제거 마지막 부분에 대한 예상"을 사용하면 대부분의 경우 여전히 작동합니다. 그렇지 않은 경우 의견). Guido의 블로그 게시물을 반드시 읽으십시오!


6
나는 여전히 이유를 이해하지 못한다 : 내부 init of First super (First, self) .__ init __ () 는 MRO가 지시하는 것이므로 Second 의 init 를 호출 합니다!
user389955

@ user389955 생성 된 객체는 모든 init 메소드를 가진 Third 유형입니다. 따라서 MRO가 모든 슈퍼 호출마다 특정 순서로 모든 초기화 함수 목록을 작성한다고 가정하면 끝까지 도달 할 때까지 한 단계 앞으로 나아갑니다.
Sreekumar R

15
나는 3 단계의 필요 더 많은 설명을 생각 : 만약이 Third상속하지 않았다 Second다음, super(First, self).__init__부를 것이다 object.__init__및 반환 후, "최초의"인쇄 될 것입니다. 그러나 때문에 Third모두로부터 상속 First하고 Second, 오히려 호출하는 것보다 object.__init__이후 First.__init__에만 최종 호출에있는 MRO의 지시 object.__init__보존되고있는 인쇄 진술 FirstSecond에 도달 할 때까지되지 않습니다 object.__init__돌아갑니다. Second마지막으로 호출 했기 때문에 object.__init__반환 Second하기 전에 내부 로 반환됩니다 First.
MountainDrew

1
흥미롭게도, PyCharm은 모르는 것 같다 모든이 (가 인식 때문에 그 힌트는, 매개 변수는 또한 입력 공분산의 몇 가지 개념을 가지고있는 슈퍼 호출. 함께 가야하는 이야기 List[subclass]A가 같은 List[superclass]경우 subclass의 서브 클래스 superclass( List으로부터 오는 typingPEP 483의 모듈
Reb

좋은 게시물이지만 생성자의 인수와 관련하여 정보가 누락되었습니다. 즉, 두 번째와 첫 번째가 다른 인수를 기대하면 어떻게됩니까? First의 생성자는 인수 중 일부를 처리하고 나머지는 Second로 전달해야합니다. 맞습니까? First가 Second에 필요한 인수에 대해 알아야한다는 것은 나에게 맞지 않습니다.
Christian K.

58

이것은 Diamond Problem 으로 알려져 있으며 페이지에는 Python에 대한 항목이 있지만 간단히 말해서 Python은 수퍼 클래스의 메소드를 왼쪽에서 오른쪽으로 호출합니다.


이것은 다이아몬드 문제가 아닙니다. 다이아몬드 문제는 4 개의 수업으로 구성되며 OP의 질문은 3 개로 만 구성됩니다.
Ian Goodfellow

147
object는 4시
GP89

28

이것은 초기화를 위해 다른 변수로 여러 상속을하고 동일한 함수 호출로 여러 MixIn을 갖는 문제를 해결하는 방법입니다. 전달 된 ** kwargs에 변수를 명시 적으로 추가하고 SuperIn의 엔드 포인트가 될 MixIn 인터페이스를 추가해야했습니다.

다음은 A확장 가능한 기본 클래스입니다 및 BC믹스 인 클래스 기능을 제공하는 사람들 모두 f. A그리고 B둘 다 매개 변수 v를 기대 __init__하고 C기대 w합니다. 이 함수 f는 하나의 매개 변수를 사용 y합니다. Q세 클래스에서 상속받습니다. MixInF의 믹스 인 인터페이스입니다 BC.


class A(object):
    def __init__(self, v, *args, **kwargs):
        print "A:init:v[{0}]".format(v)
        kwargs['v']=v
        super(A, self).__init__(*args, **kwargs)
        self.v = v


class MixInF(object):
    def __init__(self, *args, **kwargs):
        print "IObject:init"
    def f(self, y):
        print "IObject:y[{0}]".format(y)


class B(MixInF):
    def __init__(self, v, *args, **kwargs):
        print "B:init:v[{0}]".format(v)
        kwargs['v']=v
        super(B, self).__init__(*args, **kwargs)
        self.v = v
    def f(self, y):
        print "B:f:v[{0}]:y[{1}]".format(self.v, y)
        super(B, self).f(y)


class C(MixInF):
    def __init__(self, w, *args, **kwargs):
        print "C:init:w[{0}]".format(w)
        kwargs['w']=w
        super(C, self).__init__(*args, **kwargs)
        self.w = w
    def f(self, y):
        print "C:f:w[{0}]:y[{1}]".format(self.w, y)
        super(C, self).f(y)


class Q(C,B,A):
    def __init__(self, v, w):
        super(Q, self).__init__(v=v, w=w)
    def f(self, y):
        print "Q:f:y[{0}]".format(y)
        super(Q, self).f(y)

MRO는 상속 기능에 대한 다양한 인수를 다루지 않고 자체적으로 충분히 큰 주제이기 때문에 이것은 아마도 별도의 질문과 대답이어야한다고 생각합니다 (여러 상속은 특별한 경우입니다).
생명이없는

8
이론적으로는 그렇습니다. 실제로이 시나리오는 파이썬에서 다이아몬드 상속을 경험할 때마다 발생하므로 여기에 추가했습니다. 이 곳은 다이아몬드 상속을 깨끗하게 피할 수 없을 때마다가는 곳입니다. 다음은 미래의 나를 위해 몇 가지 추가 링크입니다 rhettinger.wordpress.com/2011/05/26/super-considered-super code.activestate.com/recipes/...
brent.payne는

우리가 원하는 것은 의미 적으로 의미있는 매개 변수 이름을 가진 프로그램입니다. 그러나이 예제에서는 거의 모든 매개 변수의 이름이 익명으로 지정되므로 원래 프로그래머가 코드를 문서화하고 다른 프로그래머가 코드를 읽는 것이 훨씬 어렵습니다.
Arthur


@ brhur.payne @Arthur는 전체 접근 방식 이 명명 된 매개 변수가 아닌 args/ 사용에 의존한다는 것을 의미한다고 생각합니다 kwargs.
최대

25

나는 이것이 super()질문에 직접 대답하지는 않는다는 것을 이해 하지만 공유하기에 충분하다고 생각합니다.

상속 된 각 클래스를 직접 호출하는 방법도 있습니다.


class First(object):
    def __init__(self):
        print '1'

class Second(object):
    def __init__(self):
        print '2'

class Third(First, Second):
    def __init__(self):
        Second.__init__(self)

이 방법으로 그것을 할 경우, 당신은 내가 확신으로 수동으로 각각 호출해야합니다 것을 그냥 노트 First__init__()호출되지 않습니다.


5
상속 된 각 클래스를 호출하지 않았기 때문에 호출되지 않습니다. 문제는 경우 오히려 있다는 것입니다 First그리고 Second모두가 다른 클래스를 상속 직접 다음이 일반 클래스 (다이아몬드의 시작 지점)를 호출하는 두 번 호출됩니다. 슈퍼는 이것을 피하고 있습니다.
Trilarion

@ Trilarion Yea, 그렇지 않을 것이라고 확신했습니다. 그러나, 나는 확실하게 알지 못했고 아주 가능성이 적더라도 마치 말하고 싶지 않았습니다. 그것은 object두 번 부름받는 것에 대한 좋은 지적 입니다. 나는 그것에 대해 생각하지 않았다. 부모 클래스를 직접 부르는 것을 강조하고 싶었습니다.
Seaux

불행히도, init 가 임의의 개인 메소드에 액세스하려고 시도 하면 중단됩니다 :(
Erik Aronesty

21

사무용 겉옷

모든 것이 자손이라고 가정하면 object(자신의 것이 아닌 경우) 파이썬은 클래스 상속 트리를 기반으로 MRO (method resolution order)를 계산합니다. MRO는 3 가지 속성을 충족합니다.

  • 수업의 아이들은 부모보다 먼저옵니다
  • 왼쪽 부모는 오른쪽 부모보다 먼저
  • 수업은 MRO에 한 번만 나타납니다.

이러한 순서가 없으면 Python 오류가 발생합니다. 이것의 내부 작업은 클래스 조상의 C3 라이너입니다. 여기에 대한 모든 내용을 읽으십시오.https://www.python.org/download/releases/2.3/mro/

따라서 아래 두 예에서 모두 다음과 같습니다.

  1. 아이
  2. 왼쪽
  3. 권리
  4. 부모의

메소드가 호출되면 MRO에서 해당 메소드가 처음으로 호출됩니다. 해당 메소드를 구현하지 않는 클래스는 건너 뜁니다. 에 대한 모든 호출 super메소드 내에서이 MRO의 메소드의 다음 발생을 호출합니다. 따라서 클래스를 상속 할 순서와 super메소드에서 호출하는 위치가 모두 중요합니다 .

함께 super각 방법에서 제

class Parent(object):
    def __init__(self):
        super(Parent, self).__init__()
        print "parent"

class Left(Parent):
    def __init__(self):
        super(Left, self).__init__()
        print "left"

class Right(Parent):
    def __init__(self):
        super(Right, self).__init__()
        print "right"

class Child(Left, Right):
    def __init__(self):
        super(Child, self).__init__()
        print "child"

Child() 출력 :

parent
right
left
child

super각 방법의 마지막

class Parent(object):
    def __init__(self):
        print "parent"
        super(Parent, self).__init__()

class Left(Parent):
    def __init__(self):
        print "left"
        super(Left, self).__init__()

class Right(Parent):
    def __init__(self):
        print "right"
        super(Right, self).__init__()

class Child(Left, Right):
    def __init__(self):
        print "child"
        super(Child, self).__init__()

Child() 출력 :

child
left
right
parent

에서를 Left사용하여 액세스 할 수 있습니다 . 내부 에서 액세스하고 싶다고 가정 해보십시오 . super 를 사용하여 액세스 할 수있는 방법이 있습니까? 아니면 내부에서 직접 전화해야 합니까? super()ChildRightChildRightChildRightsuper
alpha_989

4
@ alpha_989 특정 클래스의 메소드에만 액세스하려면 super를 사용하지 않고 해당 클래스를 직접 참조해야합니다. Super는 상속 클래스를 따르고 특정 클래스의 메소드를 얻는 것이 아닙니다.
Zags

1
'MRO에 클래스가 한 번만 나타납니다'를 명시 적으로 언급 해 주셔서 감사합니다. 이것은 내 문제를 해결했습니다. 이제 다중 상속이 어떻게 작동하는지 이해합니다. 누군가 MRO의 속성을 언급해야했습니다!
Tushar Vazirani

18

정보 calfzhou의 코멘트 @ , 당신이 사용할 수있는, 일반적으로,**kwargs :

온라인 실행 예

class A(object):
  def __init__(self, a, *args, **kwargs):
    print("A", a)

class B(A):
  def __init__(self, b, *args, **kwargs):
    super(B, self).__init__(*args, **kwargs)
    print("B", b)

class A1(A):
  def __init__(self, a1, *args, **kwargs):
    super(A1, self).__init__(*args, **kwargs)
    print("A1", a1)

class B1(A1, B):
  def __init__(self, b1, *args, **kwargs):
    super(B1, self).__init__(*args, **kwargs)
    print("B1", b1)


B1(a1=6, b1=5, b="hello", a=None)

결과:

A None
B hello
A1 6
B1 5

위치 적으로 사용할 수도 있습니다.

B1(5, 6, b="hello", a=None)

하지만 MRO를 기억해야합니다. 정말 혼란 스럽습니다.

나는 조금 짜증나는 될 수 있지만, 그 사람들이 사용할 때마다 잊어 발견 *args하고 **kwargs는 이러한 '마법 변수'의 몇 정말 유용하고 제정신 사용 중 하나입니다 동안 그들이하는 메소드를 오버라이드 (override) 할 때입니다.


와우 정말 못 생겼어 호출하려는 특정 수퍼 클래스를 말할 수 없다는 것은 부끄러운 일입니다. 그럼에도 불구하고 이것은 작곡을 사용하고 전염병과 같은 다중 상속을 피하는 데 더 많은 동기를 부여합니다.
Tom Busby

15

아직 다루지 않은 또 다른 요점은 클래스 초기화를 위해 매개 변수를 전달하는 것입니다. 목적지 이후super 서브 클래스 의존하기 때문에 매개 변수를 전달하는 유일한 좋은 방법은 모두 함께 묶는 것입니다. 그런 다음 다른 의미를 가진 동일한 매개 변수 이름을 가지지 않도록주의하십시오.

예:

class A(object):
    def __init__(self, **kwargs):
        print('A.__init__')
        super().__init__()

class B(A):
    def __init__(self, **kwargs):
        print('B.__init__ {}'.format(kwargs['x']))
        super().__init__(**kwargs)


class C(A):
    def __init__(self, **kwargs):
        print('C.__init__ with {}, {}'.format(kwargs['a'], kwargs['b']))
        super().__init__(**kwargs)


class D(B, C): # MRO=D, B, C, A
    def __init__(self):
        print('D.__init__')
        super().__init__(a=1, b=2, x=3)

print(D.mro())
D()

제공합니다 :

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
D.__init__
B.__init__ 3
C.__init__ with 1, 2
A.__init__

__init__더 직접적인 매개 변수 할당을 위해 수퍼 클래스를 직접 호출하는 것은 유혹적이지만super 수퍼 클래스에 호출이 있거나 MRO가 변경되고 구현에 따라 클래스 A가 여러 번 호출 .

결론 : 협동 상속과 초기화를위한 슈퍼 및 특정 매개 변수는 잘 작동하지 않습니다.


5
class First(object):
  def __init__(self, a):
    print "first", a
    super(First, self).__init__(20)

class Second(object):
  def __init__(self, a):
    print "second", a
    super(Second, self).__init__()

class Third(First, Second):
  def __init__(self):
    super(Third, self).__init__(10)
    print "that's it"

t = Third()

출력

first 10
second 20
that's it

Call to Third ()는 Third에 정의 된 init를 찾습니다 . 그리고 그 루틴에서 super를 호출하면 First에 정의 된 init 가 호출 됩니다. MRO = [제 1, 제 2]. 이제 First에 정의 된 init in init를 호출하면 MRO를 계속 검색 하고 Second에 정의 된 init를 찾습니다. super에 대한 모든 호출은 기본 오브젝트 init에 도달합니다. . 이 예제가 개념을 명확히하기를 바랍니다.

First에서 super를 호출하지 않으면 체인이 멈추고 다음과 같은 결과가 나옵니다.

first 10
that's it

1
그것은 First 클래스에서 'print'를 먼저 호출 한 다음 'super'를 호출했기 때문입니다.
rocky qi

2
그것은 호출 순서를 설명하는 것이 었습니다
Seraj Ahmad

4

pythonthehardway에서 실수하지 않으면 내장 함수 인 super ()라는 것을 배웁니다. super () 함수를 호출하면 상속이 부모와 '형제'를 통과하고 더 명확하게 볼 수 있습니다. 나는 아직 초보자이지만 python2.7 에서이 super () 사용에 대한 경험을 나누고 싶습니다.

이 페이지의 설명을 읽은 경우, MRO (Method Resolution Order)에 대해 들어 보았을 것입니다. MRO는 Depth-First-Left-to-Right 구성표를 사용하여 검색하고 실행합니다. 그것에 대해 더 많은 연구를 할 수 있습니다.

super () 함수를 추가하여

super(First, self).__init__() #example for class First.

super ()를 사용하여 여러 인스턴스와 '가족'을 연결할 수 있습니다. 그리고 메소드를 실행하고 메소드를 놓치지 않고 놓치지 않도록하십시오! 그러나 그 전후에 그것들을 추가하면 학습을 해본 적이 있는지 알게 될 것입니다.

아래 예를 들어 복사하여 붙여 넣은 다음 실행할 수 있습니다.

class First(object):
    def __init__(self):

        print("first")

class Second(First):
    def __init__(self):
        print("second (before)")
        super(Second, self).__init__()
        print("second (after)")

class Third(First):
    def __init__(self):
        print("third (before)")
        super(Third, self).__init__()
        print("third (after)")


class Fourth(First):
    def __init__(self):
        print("fourth (before)")
        super(Fourth, self).__init__()
        print("fourth (after)")


class Fifth(Second, Third, Fourth):
    def __init__(self):
        print("fifth (before)")
        super(Fifth, self).__init__()
        print("fifth (after)")

Fifth()

어떻게 작동합니까? five ()의 인스턴스는 다음과 같습니다. 각 단계는 수퍼 기능이 추가 된 클래스에서 클래스로 이동합니다.

1.) print("fifth (before)")
2.) super()>[Second, Third, Fourth] (Left to right)
3.) print("second (before)")
4.) super()> First (First is the Parent which inherit from object)

부모님이 발견되었고 계속해서 셋째와 넷째로 갈 것입니다!

5.) print("third (before)")
6.) super()> First (Parent class)
7.) print ("Fourth (before)")
8.) super()> First (Parent class)

이제 super ()가있는 모든 클래스에 액세스했습니다! 부모 클래스가 발견되어 실행되었으며 이제 상속을 계속 수행하여 코드를 완료했습니다.

9.) print("first") (Parent)
10.) print ("Fourth (after)") (Class Fourth un-box)
11.) print("third (after)") (Class Third un-box)
12.) print("second (after)") (Class Second un-box)
13.) print("fifth (after)") (Class Fifth un-box)
14.) Fifth() executed

위 프로그램의 결과 :

fifth (before)
second (before
third (before)
fourth (before)
first
fourth (after)
third (after)
second (after)
fifth (after)

나를 위해 super ()를 추가하면 파이썬이 코딩을 어떻게 실행할지 더 명확하게 알 수 있고 상속이 내가 의도 한 메소드에 액세스 할 수 있는지 확인할 수 있습니다.


자세한 데모에 감사드립니다!
Tushar Vazirani

3

@Visionscaper 가 맨 위에 말한 내용 을 추가하고 싶습니다 .

Third --> First --> object --> Second --> object

이 경우 인터프리터는 객체 클래스가 복제 되었기 때문에 객체 클래스를 필터링하지 않고 오히려 Second가 헤드 위치에 나타나고 계층 구조 하위 세트의 꼬리 위치에 나타나지 않기 때문에 필터링하지 않습니다. 객체는 꼬리 위치에만 나타나고 C3 알고리즘에서는 우선 순위를 결정하기위한 강력한 위치로 간주되지 않습니다.

클래스 C, L (C)의 선형화 (mro)는

  • 클래스 C
  • 플러스의 병합
    • 부모 P1, P2의 선형화 .. = L (P1, P2, ...) 및
    • 부모 P1, P2, .. 목록

선형화 병합은 순서가 중요하기 때문에 꼬리가 아닌 목록의 헤드로 표시되는 공통 클래스를 선택하여 수행됩니다 (아래에서 명확 해짐).

Third의 선형화는 다음과 같이 계산할 수 있습니다.

    L(O)  := [O]  // the linearization(mro) of O(object), because O has no parents

    L(First)  :=  [First] + merge(L(O), [O])
               =  [First] + merge([O], [O])
               =  [First, O]

    // Similarly, 
    L(Second)  := [Second, O]

    L(Third)   := [Third] + merge(L(First), L(Second), [First, Second])
                = [Third] + merge([First, O], [Second, O], [First, Second])
// class First is a good candidate for the first merge step, because it only appears as the head of the first and last lists
// class O is not a good candidate for the next merge step, because it also appears in the tails of list 1 and 2, 
                = [Third, First] + merge([O], [Second, O], [Second])
// class Second is a good candidate for the second merge step, because it appears as the head of the list 2 and 3
                = [Third, First, Second] + merge([O], [O])            
                = [Third, First, Second, O]

따라서 다음 코드에서 super () 구현의 경우 :

class First(object):
  def __init__(self):
    super(First, self).__init__()
    print "first"

class Second(object):
  def __init__(self):
    super(Second, self).__init__()
    print "second"

class Third(First, Second):
  def __init__(self):
    super(Third, self).__init__()
    print "that's it"

이 방법이 어떻게 해결 될지는 분명해집니다

Third.__init__() ---> First.__init__() ---> Second.__init__() ---> 
Object.__init__() ---> returns ---> Second.__init__() -
prints "second" - returns ---> First.__init__() -
prints "first" - returns ---> Third.__init__() - prints "that's it"

"두 번째가 헤드 위치에 나타나고 계층 구조 하위 세트의 꼬리 위치에 나타나지 않기 때문입니다." 머리 또는 꼬리 위치가 무엇인지, 계층 하위 집합이 무엇인지 또는 어떤 하위 집합을 참조하고 있는지 명확하지 않습니다.
OrangeSherbet

테일 위치는 클래스 계층 구조에서 더 높은 클래스를 나타내며 그 반대도 마찬가지입니다. 기본 클래스 '객체'는 꼬리 끝에 있습니다. mro 알고리즘을 이해하는 열쇠는 'Second'가 'First'의 수퍼로 나타나는 방법입니다. 우리는 일반적으로 그것을 '객체'클래스라고 가정합니다. 사실이지만, '퍼스트'클래스의 관점에서만 가능합니다. 그러나 'Third'클래스 관점에서 볼 때 'First'의 계층 순서는 다르며 위에 표시된대로 계산됩니다. mro 알고리즘은 여러 상속 된 모든 클래스에 대해이 관점 (또는 계층 구조 하위 집합)을 만들려고합니다.
supi

3

파이썬 3.5 이상에서 상속은 예측 가능하고 나에게 매우 좋습니다. 이 코드를보십시오 :

class Base(object):
  def foo(self):
    print("    Base(): entering")
    print("    Base(): exiting")


class First(Base):
  def foo(self):
    print("   First(): entering Will call Second now")
    super().foo()
    print("   First(): exiting")


class Second(Base):
  def foo(self):
    print("  Second(): entering")
    super().foo()
    print("  Second(): exiting")


class Third(First, Second):
  def foo(self):
    print(" Third(): entering")
    super().foo()
    print(" Third(): exiting")


class Fourth(Third):
  def foo(self):
    print("Fourth(): entering")
    super().foo()
    print("Fourth(): exiting")

Fourth().foo()
print(Fourth.__mro__)

출력 :

Fourth(): entering
 Third(): entering
   First(): entering Will call Second now
  Second(): entering
    Base(): entering
    Base(): exiting
  Second(): exiting
   First(): exiting
 Third(): exiting
Fourth(): exiting
(<class '__main__.Fourth'>, <class '__main__.Third'>, <class '__main__.First'>, <class '__main__.Second'>, <class '__main__.Base'>, <class 'object'>)

보시다시피, 상속 된 체인과 동일한 순서로 상속 된 각 체인에 대해 정확히 한 번 foo를 호출합니다. 전화를 걸어 주문을받을 수 있습니다 . mro :

네 번째-> 세 번째-> 첫 번째-> 두 번째-> 기본-> 오브젝트


2

어쩌면 Django rest_framework를 사용한 작은 예제와 데코레이터를 추가 할 수있는 것이 여전히 있습니다. 이것은 암묵적인 질문에 대한 답변을 제공합니다. "어쨌든 이것을 원할까요?"

말했듯이 우리는 Django rest_framework를 사용하고 일반 뷰를 사용하고 있으며 데이터베이스의 각 유형의 객체에 대해 객체 목록에 GET 및 POST를 제공하는 하나의 뷰 클래스와 GET을 제공하는 다른 뷰 클래스가 있습니다. 개별 객체에 대한, PUT 및 DELETE.

이제 POST, PUT 및 DELETE는 Django의 login_required로 장식하고 싶습니다. 이것이 두 클래스에 어떻게 영향을 미치는지 주목하십시오.

솔루션은 다중 상속을 거칠 수 있습니다.

from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required

class LoginToPost:
    @method_decorator(login_required)
    def post(self, arg, *args, **kwargs):
        super().post(arg, *args, **kwargs)

다른 방법도 마찬가지입니다.

내 구체적인 클래스의 상속 목록에서 내 LoginToPost이전 ListCreateAPIViewLoginToPutOrDelete이전을 추가 RetrieveUpdateDestroyAPIView합니다. 내 구체적인 수업 get은 장식되지 않은 채로있을 것입니다.


1

나중에 참조 할 수 있도록이 답변을 게시하십시오.

Python Multiple Inheritance는 다이아몬드 모델을 사용해야하며 함수 서명은 모델에서 변경되지 않아야합니다.

    A
   / \
  B   C
   \ /
    D

샘플 코드 스 니펫은;-

class A:
    def __init__(self, name=None):
        #  this is the head of the diamond, no need to call super() here
        self.name = name

class B(A):
    def __init__(self, param1='hello', **kwargs):
        super().__init__(**kwargs)
        self.param1 = param1

class C(A):
    def __init__(self, param2='bye', **kwargs):
        super().__init__(**kwargs)
        self.param2 = param2

class D(B, C):
    def __init__(self, works='fine', **kwargs):
        super().__init__(**kwargs)
        print(f"{works=}, {self.param1=}, {self.param2=}, {self.name=}")

d = D(name='Testing')

여기 클래스 A는 object


1
A한다 또한 호출 할 __init__. A메소드를 "발명"하지 않았 __init__으므로 다른 클래스가 AMRO에 초기에 있다고 가정 할 수 없습니다 . __init__메소드가 호출하지 않거나 호출하지 않아야 하는 유일한 클래스 super().__init__object입니다.
chepner

네. 이것이 제가 A라고 쓴 이유입니다. object아마도 내가 class A (object) : 대신 써야 합니다
Akhil Nadh PC

Aobject에 매개 변수를 추가 할 수 없습니다 __init__.
chepner
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.