ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Python] Tip - super()로 부모클래스를 초기화
    언어/파이썬 & 장고 2016. 10. 22. 22:27

    super를 사용하지 않고 자식클래스에서 부모클래스를 초기화 할 때 다음과 같은 방법을 사용합니다.

    class Parent:
        def __init__(self,num):
            print('parent', num)
    class Child(Parent):ㄷ
        def __init__(self):
            Parent.__init__(5)


    클래스가 다중상속을 받는다면 (보통 피해야되지만) 예기치 못한 동작을 일으킵니다.

    class Parent:
        def __init__(self, value):
            self.value = value
    
    class Parent1:
        def __init__(self):
            self.value *= 2
    
    class Parent2:
        def __init__(self):
            self.value += 5
    
    class Child(Parent, Parent1, Parent2):
        def __init__(self, value):
            Parent.__init__(self, value)
            Parent1.__init__(self)
            Parent2.__init__(self)
    
    foo = Child(5)
    print('(5 * 2) + 5 = ',foo.value)
    
    
    # 결과
    # (5 * 2) + 5 = 15

    위와 같이 Child 클래스는 Parent, Parent1, Parent2 클래스를 다중상속을 받은 다음 값 5를 전달하면 15라는 결과가 출력됩니다.


    다음은 같은 부모들을 다른 순서로 정의한 예입니다.

    class Parent:
        def __init__(self, value):
            self.value = value
    
    class Parent1:
        def __init__(self):
            self.value *= 2
    
    class Parent2:
        def __init__(self):
            self.value += 5
    
    class Child(Parent, Parent2, Parent1):
        def __init__(self, value):
            Parent.__init__(self, value)
            Parent1.__init__(self)
            Parent2.__init__(self)
    
    foo = Child(5)
    print('(5 * 2) + 5 = ',foo.value)
    # 결과
    # 15

    하지만 부모 클래스 생성자 Parent2.__init__, Parent1.__init__을 이전과 같은 순서로 호출합니다. 이 클래스의 동작은 부모 클래스를 정의한 순서와 일치하지 않습니다. (파라미터순서는 상관없는거 같은데..)


    다른 문제는 다이아몬드 상속입니다. 다이아몬드 상속은 서브클래스가 계층 구조에서 같은 슈퍼클래스를 둔 서로 다른 두 클래스에서 상속받을 때 발생합니다. 다이아몬드 상속은 공통 슈퍼클래스의 __init__메서드를 여러 번실행하게 해서 예상치 못한 동작을 일으킵니다.

    class Parent:
        def __init__(self, value):
            self.value = value
    
    class Parent1:
        def __init__(self):
            self.value *= 2
    
    class Parent2:
        def __init__(self):
            self.value += 5
    
    class Child(Parent):
        def __init__(self, value):
            Parent.__init__(self, value)
            self.value *= 5
    
    class Child1(Parent):
        def __init__(self, value):
            Parent.__init__(self, value)
            self.value += 2
    
    class This(Child, Child1):
        def __init__(self, value):
            Child.__init__(self, value)
            Child1.__init__(self, value)
    
    foo = This(5)
    print('(5 * 5) + 2 = 27 but is ', foo.value)
    
    # 결과
    # ('(5 * 5) + 2 = 27 but is ', 7)

    결과는 27이여야 하지만 두 번째 부모클래스의 생성자 Child1.__init__를 호출하는 코드가 있어서 Parent.__init__가 두 번째 호출될 때 self.value를 다시 5로 리셋합니다.


    다이아몬드 상속이란?


    이러한 그림과 같이 상속이 이뤄진 코드를 아래라고 가정하고 실행시키면 문제가 발생

    class A:
        def __init__(self):
            print("A 생성자 호출")
             
    class B(A):
        def __init__(self):
            print("B 생성자 호출")
            A.__init__(self)
         
    class C(A):
        def __init__(self):
            print("C 생성자 호출")
            A.__init__(self)
         
    class D(B, C):
        def __init__(self):
            print("D 생성자 호출")
            B.__init__(self)
            C.__init__(self)
    
    
    d = D()
    # 결과
    # D 클래스의 생성자 호출!
    # B 클래스의 생성자 호출!
    # A 클래스의 생성자 호출!
    # C 클래스의 생성자 호출!
    # A 클래스의 생성자 호출!
    

    위와 같이 A클래스가 2번 생성이 되는 문제가 발생. 이러한 문제를 다이아몬드 상속문제라 함.


    이러한 문제를 해결하기 위해 super라는 내장 함수를 추가하고 메서드 해석 순서(MRO, Method Resolution Order)를 정의했습니다. MRO는 어떤 슈퍼클래스부터 초기화하는지를 정합니다. 또한 다이아몬드 계층 구조에 있는 공통 슈퍼클래스를 단 한번만 실행하게 합니다.

    파이썬 3에서는 super를 인수 없이 호출하면 __class__와 self를 인수로 넘겨서 호출한 것으로 처리가 됩니다. 파이썬 3에서는 항상 super를 사용해야 합니다. super는 명확하고 간결하며 항상 제대로 동작하기 때문입니다.

    class Child(Parent):
        def __init__(self, value):
            super(__class__,self).__init__(value*2)
    
    class Child1(Parent):
        def __init__(self, value):
            super().__init__(value*2)
    
    assert Child(10).value == Child1(10).value


    파이썬 3에서는 __class__ 변수를 사용한 메서드에서 현재 클래스를 올바르게 참조하도록 해주므로 위 코드가 잘 동작합니다. 

    요약

    파이썬 표준 메서드 해석 순서(MRO)는 슈퍼클래스의 초기화 순서와 다이아몬드 상속 문제를 해결함
    항상 내장함수 super로 부모클래스를 초기화


    댓글