etc./StackOverFlow

__init__() 메서드로 Python super() 이해하기 [중복]

청렴결백한 만능 재주꾼 2021. 11. 11. 07:58
반응형

질문자 :Mizipzor


super() 의 사용을 이해하려고 노력하고 있습니다. 보기에는 두 자식 클래스가 모두 생성될 수 있습니다.

다음 2개의 하위 클래스의 실제 차이점이 무엇인지 궁금합니다.

 class Base(object): def __init__(self): print "Base created" class ChildA(Base): def __init__(self): Base.__init__(self) class ChildB(Base): def __init__(self): super(ChildB, self).__init__() ChildA() ChildB()


super() 사용하면 기본 클래스를 명시적으로 참조하는 것을 피할 수 있습니다. 그러나 주요 이점은 모든 종류의 재미있는 일 이 발생할 수 있는 다중 상속과 함께 제공됩니다. super에서 아직 표준 문서를 참조하지 않은 경우 참조하십시오.

Python 3.0에서 구문이 변경 되었음을 유의하십시오. IMO가 훨씬 더 좋은 super(ChildB, self).__init__() 대신 super().__init__() 라고 말할 수 있습니다. 표준 문서는 또한 매우 설명적인 super() 사용에 대한 가이드를 참조합니다.


Kiv

super() 를 이해하려고 노력 중이다.

super 를 사용하는 이유는 협력 다중 상속을 사용할 수 있는 자식 클래스가 MRO(메소드 해결 순서)에서 올바른 다음 부모 클래스 함수를 호출하기 때문입니다.

Python 3에서는 다음과 같이 호출할 수 있습니다.

 class ChildB(Base): def __init__(self): super().__init__()

Python 2에서는 정의하는 클래스의 이름과 self super 를 호출해야 했지만 중복되고 느리고(이름 조회로 인해) 더 장황하기 때문에 지금부터 이것을 피할 것입니다(따라서 Python을 업데이트하십시오. 아직 하지 않았다면!):

 super(ChildB, self).__init__()

super가 없으면 다음 부모의 호출을 하드 와이어로 연결하기 때문에 다중 상속을 사용하는 능력이 제한됩니다.

 Base.__init__(self) # Avoid this.

아래에서 추가로 설명합니다.

"이 코드에는 실제로 어떤 차이점이 있습니까?:"

 class ChildA(Base): def __init__(self): Base.__init__(self) class ChildB(Base): def __init__(self): super().__init__()

이 코드의 주요 차이점은 ChildB super __init__ 에서 간접 참조 계층을 얻는다는 것입니다. 이 계층은 정의된 클래스를 사용하여 MRO에서 조회할 __init__

정식 질문인 Python에서 'super'를 사용하는 방법 에 대한 답변에서 이 차이점을 설명합니다. 종속성 주입협력 다중 상속 을 보여줍니다.

파이썬에 super

super 와 거의 동일한 코드입니다(C에서 구현되는 방식, 일부 검사 및 대체 동작 제외, Python으로 변환):

 class ChildB(Base): def __init__(self): mro = type(self).mro() check_next = mro.index(ChildB) + 1 # next after *this* class. while check_next < len(mro): next_class = mro[check_next] if '__init__' in next_class.__dict__: next_class.__init__(self) break check_next += 1

네이티브 Python처럼 조금 더 작성:

 class ChildB(Base): def __init__(self): mro = type(self).mro() for next_class in mro[mro.index(ChildB) + 1:]: # slice to end if hasattr(next_class, '__init__'): next_class.__init__(self) break

super 객체가 없다면 메서드 해결 순서에서 적절한 다음 메서드를 호출하도록 이 수동 코드를 모든 곳에 작성해야 합니다(또는 다시 생성해야 합니다!).

수퍼는 호출된 메서드의 클래스와 인스턴스를 명시적으로 알리지 않고 Python 3에서 이 작업을 어떻게 수행합니까?

호출 스택 프레임을 가져오고 클래스(암시적으로 로컬 자유 변수 __class__ 로 저장되어 호출 함수를 클래스에 대한 클로저로 만듭니다)와 해당 함수에 대한 첫 번째 인수를 찾습니다. 사용할 MRO(Method Resolution Order)입니다.

MRO에 대한 첫 번째 인수가 필요하기 때문에 super 를 사용하는 것은 호출된 클래스의 MRO에 액세스할 수 없기 때문에 불가능 합니다.

다른 답변에 대한 비판:

super()를 사용하면 기본 클래스를 명시적으로 참조하는 것을 피할 수 있습니다. . 그러나 주요 이점은 모든 종류의 재미있는 일이 발생할 수 있는 다중 상속과 함께 제공됩니다. 아직 보지 않았다면 super의 표준 문서를 참조하십시오.

다소 손이 많이 가고 많은 것을 알려주지 않지만 super 의 요점은 부모 클래스를 작성하는 것을 피하는 것이 아닙니다. 요점은 MRO(메소드 확인 순서)의 줄에 있는 다음 메서드가 호출되도록 하는 것입니다. 이것은 다중 상속에서 중요해집니다.

여기에서 설명하겠습니다.

 class Base(object): def __init__(self): print("Base init'ed") class ChildA(Base): def __init__(self): print("ChildA init'ed") Base.__init__(self) class ChildB(Base): def __init__(self): print("ChildB init'ed") super().__init__()

그리고 Child 다음에 호출할 종속성을 생성해 보겠습니다.

 class UserDependency(Base): def __init__(self): print("UserDependency init'ed") super().__init__()

이제 기억 ChildB , 슈퍼 사용 ChildA 되지 않습니다 :

 class UserA(ChildA, UserDependency): def __init__(self): print("UserA init'ed") super().__init__() class UserB(ChildB, UserDependency): def __init__(self): print("UserB init'ed") super().__init__()

그리고 UserA 는 UserDependency 메서드를 호출하지 않습니다.

 >>> UserA() UserA init'ed ChildA init'ed Base init'ed <__main__.UserA object at 0x0000000003403BA8>

그러나 UserB 는 실제로 super ChildB 호출합니다.

 >>> UserB() UserB init'ed ChildB init'ed UserDependency init'ed Base init'ed <__main__.UserB object at 0x0000000003403438>

다른 답변에 대한 비판

어떤 경우에도 ChildB를 하위 클래스로 분류할 때 오류가 발생하므로 다른 답변에서 제안하는 다음을 수행해서는 안 됩니다.

 super(self.__class__, self).__init__() # DON'T DO THIS! EVER.

(그 답변은 영리하지도, 특별히 흥미롭지도 않지만, 댓글에 대한 직접적인 비판과 17개 이상의 반대에도 불구하고 답변자는 친절한 편집자가 문제를 해결할 때까지 계속 제안했습니다.)

설명: super() 에서 클래스 이름 대신 self.__class__ 를 사용하면 재귀가 발생합니다. super 사용하면 자식 클래스에 대해 MRO에서 다음 부모를 찾을 수 있습니다(이 답변의 첫 번째 섹션 참조). super 에게 우리가 자식 인스턴스의 메서드에 있다고 말하면, 다음 메서드(아마도 이 메서드)를 조회하여 재귀를 발생시키고 아마도 논리적 실패(응답자의 예에서는 그렇게 함) 또는 RuntimeError 일으킬 것입니다. 재귀 깊이가 초과되었습니다.

 >>> class Polygon(object): ... def __init__(self, id): ... self.id = id ... >>> class Rectangle(Polygon): ... def __init__(self, id, width, height): ... super(self.__class__, self).__init__(id) ... self.shape = (width, height) ... >>> class Square(Rectangle): ... pass ... >>> Square('a', 10, 10) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in __init__ TypeError: __init__() missing 2 required positional arguments: 'width' and 'height'

인수가 없는 Python 3의 새로운 super() 호출 메서드를 사용하면 다행스럽게도 이 문제를 피할 수 있습니다.


Aaron Hall

Python 3.0 이상에서는 다음을 사용할 수 있습니다.

 super().__init__()

간결하고 부모 OR 클래스 이름을 명시적으로 참조할 필요가 없으므로 편리할 수 있습니다. Python 2.7 이하의 경우 일부 사람들은 클래스 이름 대신 self.__class__ 를 작성하여 이름을 구분하지 않는 동작을 구현한다는 점을 추가하고 싶습니다.

 super(self.__class__, self).__init__() # DON'T DO THIS!

self.__class__ 가 자식 클래스를 반환할 수 있는 클래스에서 상속하는 모든 클래스에 대해 super 대한 호출을 중단합니다. 예를 들어:

 class Polygon(object): def __init__(self, id): self.id = id class Rectangle(Polygon): def __init__(self, id, width, height): super(self.__class__, self).__init__(id) self.shape = (width, height) class Square(Rectangle): pass

Rectangle 의 하위 클래스인 Square 클래스가 있습니다. Rectangle 대한 생성자 Square 대해 별도의 생성자를 작성하고 싶지 않다고 가정해 보겠습니다. 그러나 어떤 이유로든 Square를 구현하여 다른 방법을 다시 구현할 수 있기를 원합니다.

mSquare = Square('a', 10,10) 사용하여 Square 를 만들 때 Square 에 자체 생성자를 제공하지 않았기 때문에 Rectangle 대한 생성자를 호출합니다. Rectangle 생성자에서 super(self.__class__,self) mSquare 의 슈퍼클래스를 반환 Rectangle 생성자를 다시 호출합니다. 이것이 @S_C가 언급했듯이 무한 루프가 발생하는 방식입니다. 이 경우, 내가 super(...).__init__() Rectangle 대한 생성자를 호출하고 있지만 인수를 제공하지 않기 때문에 오류가 발생합니다.


AnjoMan

슈퍼는 부작용이 없다

 Base = ChildB Base()

예상대로 작동

 Base = ChildA Base()

무한 재귀에 들어간다.


S C

참고로... Python 2.7에서는 super() 가 버전 2.2에서 도입된 object 상속하는 클래스에서 상속하는 경우 super() 호출할 수 있습니다. ).

개인적으로 python 2.7 코드의 경우 super() 사용의 이점을 얻을 때까지 BaseClassName.__init__(self, args) 을 계속 사용할 것입니다.


rgenito

정말 없습니다. super() 는 메서드를 호출하기 위해 MRO(메서드 확인 순서, cls.__mro__ 그냥 기본 전화 __init__ 기본 호출 __init__ . MRO에는 정확히 하나의 항목이 있습니다. 바로 기본입니다. 그래서 당신은 정말로 똑같은 일을 하고 있지만 super() 를 사용하여 더 좋은 방법으로(특히 나중에 다중 상속에 들어갈 경우).


Devin Jeanpierre

주요 차이점은 ChildA.__init__ 는 무조건 Base.__init__ 호출하는 반면 ChildB.__init__ self 의 조상 라인 ChildB 조상이 되는 모든 클래스 에서 __init__ 를 호출한다는 것입니다(예상한 것과 다를 수 있음).

다중 상속을 사용 ClassC 를 추가하는 경우:

 class Mixin(Base): def __init__(self): print "Mixin stuff" super(Mixin, self).__init__() class ChildC(ChildB, Mixin): # Mixin is now between ChildB and Base pass ChildC() help(ChildC) # shows that the Method Resolution Order is ChildC->ChildB->Mixin->Base

그러면 Base 는 더 이상 ChildC 인스턴스에 대한 ChildB 이제 super(ChildB, self) selfChildC 인스턴스인 경우 Mixin 을 가리킵니다.

ChildBBase 사이 Mixin 을 삽입했습니다. super() 하여 이를 활용할 수 있습니다.

따라서 Cooperative Multiple Inheritance 시나리오에서 사용할 수 있도록 클래스를 설계한 경우 런타임에 누가 조상이 될지 모르기 때문에 super

슈퍼 고려 슈퍼 포스트pycon 2015 관련 비디오가 이것을 꽤 잘 설명합니다.


RubenLaguna

출처 : http:www.stackoverflow.com/questions/576169/understanding-python-super-with-init-methods

반응형