본문 바로가기

Python

Python: class inheritance MRO, super # method resolution order # super # 상속 순서 # 부모 클래스 # 초기화

728x90
반응형

Python에서는

class에서 상속받을 시, method가 겹칠 때, 어떤 순서로 method를 적용시킬지를 정해놓았는데

이를 MRO(Method Resolution Order)이라 한다.

 

__mro__

CLASS.__mro__ 로 순서를 확인할 수 있다.

 

class A: pass

class B: pass

class C(A,B): pass

print(C.__mro__)

# (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)

 

위의 결과를 보면 C, A, B 순으로 되어 있는 것을 확인할 수 있다.

 

* 다양한 상속 환경에서 method 실행해보기

class A:
    def method(self):
        print('from A')

    def parent_method(self):
        print('parent from A')

class B:
    def method(self):
        print('from B')

    def parent_method(self):
        print('parent from B')

    def only_b_method(self):
        print('only from B')

class C(A,B):
    def method(self):
        print('from C')

c = C()
c.method()
c.parent_method()
c.only_b_method()

# from C
# parent from A
# only from B

 

1) def method(self) - (A, B, C 보유)

MRO: C, A, B에 따라서 C에 있는 method를 먼저 불러온다. (# from C)

 

2) def parent_method(self) - (A, B 보유)

C에는 없는 메소드이므로 부모클래스의 A,B에서 찾아서 가져온다.

A, B 모두에 있으나 MRO: C, A, B에 따라서 C는 없어서 다음 A에서 찾아서 실행 (# parent from A)

 

3) def only_b_method(self) - (B만 보유)

C에는 없는 method이므로 부모클래스의 A,B에서 찾아서 가져온다.

A에도 없음

B만 보유한 메소드

MRO: C, A, B에 따라서 C, A는 없어서 B에서 찾아서 실행 (# only from B)

 

super()로 상속받은 클래스의 메소드 사용하기

class A:
    def method(self):
        print('from A')

class B:
    def method(self):
        print('from B')

class C(A,B):
    def method(self):
        super().method()

c = C()
c.method()

# from A

 

C의 instance c가 method()를 실행하면 super().method()를 실행하고 MRO에 따라서 A의 method를 실행 (# from A)

 

여기서 super()는 MRO를 따른다는 것을 명심해야 한다.

super()는 상속받은 부모클래스를 가리키는 것이 아니라, MRO를 따르고, MRO상의 다음 클래스를 가리킨다.

다음 예를 통해 확인해보자

class X:
    def method(self):
        print("Super X")

class A(X):
    def method(self):
        super().method()

class B(X):
    def method(self):
        print('from B')

class C(A,B):
    def method(self):
        super().method()

c = C()
c.method()

# from B

 

결과 예측

C의 method가 super().method()를 실행

    C는 A에서 먼저 method를 찾으니까 A의 method를 실행

A의 method가 super().method()를 실행

    A는 X를 상속 받았으므로 X의 method를 실행

        결과: Super X

 

위와 같이 실행되는 것이 아니다.

super()가 상속받은 클래스를 가리킨다는 개념으로 접근하면 당연히 위와 같아야 될거 같은데 그렇지가 않다.

 

파이썬에서의 super()는 상속받은 클래스를 가리키는 것이 아니라

MRO 상의 다음 클래스를 가리키는 것이다.

 

c 는 C의 instance임으로 c.method()가 실행되게 되는 C의 MRO를 살펴보면 다음과 같다.

print(C.__mro__)

# (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.X'>, <class 'object'>)
# 즉, C -> A -> B -> X

 

따라서 MRO에 따라서 super().method()가 적용되는 것을 다시 정리하면 다음과 같다

 

C의 method가 super().method()를 실행

    C는 A에서 먼저 method를 찾으니까 A의 method를 실행

A의 method가 super().method()를 실행

    A는 X를 상속 받았으므로 X의 method를 실행 MRO상 A의 다음은 B이므로 B의  method를 실행

        결과: Super X from B

 

요약: 파이썬의 super()는 상속받은 class를 가리키는 것이 아니라, MRO 상의 다음 class를 가리킨다.

 

 

+ 상속받은 class들의 생성자 인자가 다를 때, 부모 클래스의 생성자 초기화 방법

   - different __init__ arguments

# 1. using super()
class A:
    def __init__(self, a, **kwargs):
        super().__init__(**kwargs)
        self.a = a

class B:
    def __init__(self, b, **kwargs):
        super().__init__(**kwargs)
        self.b = b

class C(A, B):
    def __init__(self, a, b, c):
        super().__init__(a=a, b=b)
        self.c = c

# 2. 
class A:
    def __init__(self, a):
        self.a = a

class B:
    def __init__(self, b):
        self.b = b

class C(A, B):
    def __init__(self, a, b, c):
        A.__init__(self, a)
        B.__init__(self, b)
        self.c = c

 

1의 super()를 이용하는 방법

  - 부모 클래스들이 여러 인자를 받을수 있도록 **kwargs 사용

 

2. 부모 클래스를 직접 부르는 방법

  - 기존 부모 클래스가 **kwargs 인자 없이 선언되어 있을 때의 수정을 가하지 않고 할 수 있다.

  - 어떤 부모 클래스를 먼저 초기화 하느냐에 따라서 클래스 변수 등이 오버라이딩 되는 것이 달라질 수 있으므로 주의

   (짜는 순서에 따라 MRO와 다르게 오버라이딩 될 수 있으니 주의해야겠다.)

728x90
반응형