python

Python 다중 상속, super()

Jueuunn7 2024. 9. 2. 17:52

1. 다중 상속

다중상속은 하나의 클래스가 2개 이상의 클래스를 상속받아 자식 클래스를 작성하는것을 말한다.

클래스를 생성할때 ()에 클래스 이름을 넣어서 다중상속 클래스를 만든다.

>>> class A:
...     def hello():
...             print('hello')
...
>>> class B:
...     def bye():
...             print('bye')
...
>>> class C(A, B):
...     pass
...
>>>
>>> C.hello()
hello
>>> C.bye()
bye

클래스 C는 클래스 A, B의 메서드인 hello, bye 메서드 모두를 갖고 호출할 수 있다.

이걸 그림을 간단하게 표현하면 이렇게 볼 수 있다.

 

2. 다이아몬드 상속

>>> class A:
...     def hello():
...             print('hello')
...
>>> class B(A):
...     def hello():
...             print('hello class B')
...
>>> class C(A):
...     def hello():
...             print('hello class C')
...
>>> class D(B, C):
...     pass
...

이젠 최상위 클래스인 A를 B, C클래스가 상속받고, D클래스는 B, C 클래스를 상속받는다. 이 상속받는 구조를 다이아몬드 상속이라고 한다.

이걸 그림으로 표현해보면 이렇게 된다.

하지만 이런 방식으로 상속을 받으면 문제가 생긴다.

클래스 D에서 hello 메서드를 호출하면 B.hello를 호출할지, C.hello를 호출할지 모른다.

일단 호출해본다.

>>> D.hello()
hello class B

B.hello가 호출되는걸 볼 수 있다. 왜 A도 아니고 C도 아니고 B를 호출할까?

 

>>> class A:
...     def hello(self):
...         print("Hello class A")
...
>>> class B(A):
...     def hello(self):
...         super().hello()
...
>>> class C(A):
...     def hello(self):
...         print("Hello class C")
...
>>>
>>> class D(B, C):
...     pass

 

 

이 경우에는 클래스 B의 hello 메서드가 super().hello() 즉, 부모의 hello 메서드를 가르키고 있다. 이런 경우에서는 D클래스에서 hello메서드를 호출하면 과연 어떤 hello가 호출될까? 

아마 B클래스가 호출될것이고, B클래스는 A클래스의 hello를 가리키니까 A.hello()가 호출되는것처럼 보인다.

하지만 실제로는 C클래스가 갑자기 호출되는것을 볼 수 있다.

>>> ins = D()
>>> ins.hello()
Hello class C

 

3. MRO

Method Resolution Order

MRO는 자식클래스에서 부모클래스의 어떤 메서드를 어떤 순서로 호출할지 정해준다.

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

클래스 D의 __mro__를 호출해보면 자신 클래스, B, C, A 순서대로 정해져 있는것을 볼 수 있다. 이 순서대로 메서드를 호출한다.

super()을 사용할때는 좀 특이하게 작동한다.

B클래스의 hello는 super().hello() 즉 A.hello()를 가리키고 있어서 결국 A클래스의 메서드가 실행되는 것과 같다. 하지만 mro는 A클래스보다 C클래스가 우선순위가 높기에 C클래스의 hello 메서드를 실행하는 것이다.

 

MRO는 C3 Linearization을 기준으로 순서가 정해진다.

1. 자식 클래스가 부모 클래스보다 먼저 온다.

2. 부모 클래스가 다중 상속된 경우 부모 클래스 순서는 상속 선언의 순서를 따른다.

3. 각 클래스는 MRO에서 한 번만 나타내야 한다.

이 규칙을 따라서 mro의 순서가 정해진다. 프로그래밍할때 mro를 직접 변경하는 방법은 없지만 호출하는 방식을 변경해서 mro 순서와 다른 클래스를 호출할 수 있다.

class A:
	def __init__(self, name):
    	self.name = name
        
class B:
	def __init__(self, age):
    	self.age = age
        
class C(A, B):
	def __init__(self, name, age):
    	super().__init__(name)
        super(A, self).__init__(age)

첫번째 super()은 mro 순서에 따라서 A 클래스를 초기화하고, 두번째 super()이 A의 다음 클래스인 B 클래스를 찾아 초기화시켜준다.
이 방식대로 해서 초기화할 수 있다.