파이썬

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

zooheon 2022. 8. 22. 23:05
반응형

사용되는 이유는 무엇 super()입니까?

Base.__init__를 사용하는 것과 차이가 super().__init__있습니까?

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().__init__()대신 super(ChildB, self).__init__()어느 것이 훨씬 더 낫다고 말할 수 있습니다 . 표준 문서는 또한 매우 설명적인 사용 가이드를 참조합니다.super()

 

이해하려고 노력 중이야super()

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

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

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

Python 2에서는 super정의하는 클래스의 이름을 사용하여 이와 같이 호출 self해야 했습니다. 아직 없습니다!):

        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__()

이 코드의 주요 차이점은 MRO에서 조회할 다음 클래스를 결정하기 위해 정의된 클래스를 사용하는 with ChildB에서 간접 참조 계층을 얻는다는 것입니다.__init__super__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메서드 해결 순서에서 적절한 next 메서드를 호출할 수 있도록 이 수동 코드를 모든 곳에 작성해야 합니다(또는 다시 생성해야 합니다!).

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

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

MRO에 대한 첫 번째 인수가 필요하므로 호출된 클래스의 MRO에 대한 액세스 권한이 없기 때문에 정적 메서드와 함께 사용super 하는 것은 불가능 합니다.

다른 답변에 대한 비판:

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__()

이제 ChildBsuper를 사용하면 다음을 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__()

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

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

그러나 UserB실제로 다음을 호출하기 때문에 UserDependency를 ChildB호출합니다 super.

>>> 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개 이상의 반대에도 불구하고 답변자는 친절한 편집자가 문제를 해결할 때까지 계속 제안했습니다.)

설명: self.__class__의 클래스 이름을 대신 하여 사용하면 super()재귀가 발생합니다. 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 클래스 이름을 명시적으로 참조할 필요가 없으므로 편리할 수 있습니다. self.__class__Python 2.7 이하의 경우 일부 사람들 은 클래스 이름 대신 작성하여 이름을 구분하지 않는 동작을 구현한다는 점을 추가하고 싶습니다.

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

그러나 이것은 하위 클래스를 반환할 수 있는 super클래스에서 상속된 모든 클래스에 대한 호출을 중단합니다. self.__class__예를 들어:

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에 의 하위 클래스인 클래스가 Rectangle있습니다. 에 대한 생성자 가 충분 Square하기 때문에 에 대한 별도의 생성자를 작성하고 싶지 않지만 어떤 이유로든 다른 방법을 다시 구현할 수 있도록 Square를 구현하고 싶습니다.Rectangle

내가 Squareusing mSquare = Square('a', 10,10)을 만들 때 Python 은 자체 생성자 Rectangle를 제공하지 않았기 때문에 에 대한 생성자를 호출합니다. Square그러나 에 대한 생성자 Rectangle에서 호출 super(self.__class__,self)은 의 수퍼클래스를 반환 mSquare하므로 에 대한 생성자를 Rectangle다시 호출합니다. 이것이 @S_C가 언급했듯이 무한 루프가 발생하는 방식입니다. 이 경우 실행할 때 super(...).__init__()생성자를 호출 Rectangle하지만 인수를 제공하지 않으므로 오류가 발생합니다.

 

반응형