WeniVooks

검색

견고한 파이썬

다중 상속

1. 다중 상속

다중 상속은 한 클래스가 여러 부모 클래스로부터 상속을 받는 것을 의미합니다. 이를 통해 여러 부모 클래스의 특성과 기능을 하나의 자식 클래스에 결합할 수 있습니다. 기본 골격은 아래와 같습니다.

class Parent1:
    pass
 
class Parent2:
    pass
 
class Child(Parent1, Parent2):
    pass
class Parent1:
    pass
 
class Parent2:
    pass
 
class Child(Parent1, Parent2):
    pass

Child 클래스는 Parent1Parent2 두 부모 클래스로부터 상속을 받습니다.

2. 다중 상속 예제

다중 상속을 사용할 때 가장 중요한 문제 중 하나는 메서드의 호출 순서입니다. 만약 두 부모 클래스가 동일한 메서드 이름을 가지고 있다면, 자식 클래스 객체에서 해당 메서드를 호출할 때 어느 부모의 메서드를 호출해야 하는지 결정해야 합니다.

파이썬은 이 문제를 해결하기 위해 Method Resolution Order(MRO)를 도입하였고, mro()에 따라 호출 순서를 결정합니다.

class A: def method(self): print("A method") class B(A): def method(self): print("B method") class C(A): def method(self): print("C method") class D(B, C): pass obj = D() print(D.mro()) # 출력: [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>] obj.method() # 출력: B method

여기서 D(B, C)D(C, B)로 바꾸면 호출되는 값이 달라집니다. 따라서 obj.method()를 출력하면 mro 순서에서 자신에게 가장 가까운 곳에 정의되어 있는 메서드를 가지고 오게 됩니다.

class A: def method(self): print("A method") class B(A): def method(self): print("B method") class C(A): def method(self): print("C method") class D(C, B): pass obj = D() print(D.mro()) # 출력: [<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>] obj.method() # 출력: C method

위 상속 관계는 아래처럼 그림으로 표현할 수 있습니다.

이러한 형태는 마치 다이아몬드처럼 생겼다 하여 다이아몬드 상속이라 불립니다. 이 다이아몬드 상속에서 주의해야 할 점은 python 2.x와 3.x가 순서가 다르다는 것입니다. 3.x에서는 hello C가, 2.x에서는 hello A가 출력됩니다. 다만 2.x에서 class A:class A(object):로 바꾸면 실행 순서는 같아집니다. 상속 파트에서 언급한 것처럼 python 2.x에서는 object를 명시적으로 써주어야 새로운 스타일의 object를 상속하기 때문입니다.

class A: def hello(self): print('hello A') class B(A): pass class C(A): def hello(self): print('hello C') class D(B, C): pass d = D() d.hello()

조금 어려운 문제를 살펴보도록 하겠습니다. 앞서 다뤘던 Car class를 이용해 아래 그림과 같은 다중 상속을 구현해보도록 하겠습니다.

이 순서를 잘 보시면 HybridCar는 상속을 받을 이유가 없습니다. ElectricCar에서 이미 HybridCar를 상속받고 있기 때문입니다. 그럼에도 불구하고 상속을 받아보도록 하겠습니다.

class Car(object):
    pass
 
class HybridCar(Car):
    pass
 
class ElectricCar(HybridCar):
    pass
 
class HybridElectricCar(ElectricCar, HybridCar):
    pass
 
modelz = HybridElectricCar()
class Car(object):
    pass
 
class HybridCar(Car):
    pass
 
class ElectricCar(HybridCar):
    pass
 
class HybridElectricCar(ElectricCar, HybridCar):
    pass
 
modelz = HybridElectricCar()

위처럼 작성 되었을 때에는 문제가 생기지 않습니다. 이는 MRO에 문제가 생기지 않기 때문입니다. 다만 아래와 같이 순서를 바꾸면 에러가 발생됩니다. ElectricCar의 부모인 HybridCar를 먼저 참조했기 때문입니다. 부모와 조부모(부모의 부모)가 함께 상속되었는데 조부모(부모의 부모)를 먼저 참조하는 것은 MRO 순서에 어긋나게 됩니다.

class Car(object): pass class HybridCar(Car): pass class ElectricCar(HybridCar): pass class HybridElectricCar(HybridCar, ElectricCar): pass modelz = HybridElectricCar()
# 출력
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-8-271ee34d39a5> in <cell line: 23>()
     21         print(x, '스피드로 달리고 있습니다.')
     22
---> 23 class HybridElectricCar(HybridCar, ElectricCar):
     24     pass
     25
 
TypeError: Cannot create a consistent method resolution
order (MRO) for bases HybridCar, ElectricCar
 
# MRO : 어떤 클래스를 어떤 순서로 받을 것인지의 선택을 말합니다.
# 출력
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-8-271ee34d39a5> in <cell line: 23>()
     21         print(x, '스피드로 달리고 있습니다.')
     22
---> 23 class HybridElectricCar(HybridCar, ElectricCar):
     24     pass
     25
 
TypeError: Cannot create a consistent method resolution
order (MRO) for bases HybridCar, ElectricCar
 
# MRO : 어떤 클래스를 어떤 순서로 받을 것인지의 선택을 말합니다.
{"packages":["numpy","pandas","matplotlib","lxml"]}
10.5 상속10.7 클래스 심화