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
를 사용하는 이유는 협력 다중 상속을 사용할 수 있는 자식 클래스가 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()
호출 메서드를 사용하면 다행스럽게도 이 문제를 피할 수 있습니다.
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
대한 생성자를 호출하고 있지만 인수를 제공하지 않기 때문에 오류가 발생합니다.
참고로... Python 2.7에서는 super()
가 버전 2.2에서 도입된 object
상속하는 클래스에서 상속하는 경우 super()
호출할 수 있습니다. ).
개인적으로 python 2.7 코드의 경우 super()
사용의 이점을 얻을 때까지 BaseClassName.__init__(self, args)
을 계속 사용할 것입니다.
정말 없습니다. super()
는 메서드를 호출하기 위해 MRO(메서드 확인 순서, cls.__mro__
그냥 기본 전화 __init__
기본 호출 __init__
. MRO에는 정확히 하나의 항목이 있습니다. 바로 기본입니다. 그래서 당신은 정말로 똑같은 일을 하고 있지만 super()
를 사용하여 더 좋은 방법으로(특히 나중에 다중 상속에 들어갈 경우).
주요 차이점은 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)
self
가 ChildC
인스턴스인 경우 Mixin
을 가리킵니다.
ChildB
와 Base
사이 Mixin
을 삽입했습니다. super()
하여 이를 활용할 수 있습니다.
따라서 Cooperative Multiple Inheritance 시나리오에서 사용할 수 있도록 클래스를 설계한 경우 런타임에 누가 조상이 될지 모르기 때문에 super
슈퍼 고려 슈퍼 포스트 와 pycon 2015 관련 비디오가 이것을 꽤 잘 설명합니다.
출처 : http:www.stackoverflow.com/questions/576169/understanding-python-super-with-init-methods