파이썬에서 다중 상속

휴먼스케이프

안녕하세요. 휴먼스케이프의 개발자 Bruno입니다.

이 번 포스트에서는 파이썬에서 다중 상속을 어떻게 사용해야 하며, 이 때 다중 상속에서 발생하는 문제를 해결하기 위해 믹스인을 어떻게 활용하는지 다루겠습니다.

다중 상속의 문제

다중 상속은 슈퍼클래스에 같은 메소드가 정의되어 있을 때, 어떤 메소드를 호출해야 하는지 모호해지는 문제가 있습니다.

파이썬은 상속 그래프를 특정 순서에 따라서 조회합니다. 이 순서를 Method Resolution Order(MRO), 즉 메서드 결정 순서 라고 합니다. __mro__ 속성은 현재 클래스부터 object 클래스까지 슈퍼 클래스들의 MRO를 튜플 형태로 저장합니다.

MRO는 서브클래스 정의에 나열된 슈퍼클래스들의 순서도 고려합니다. 이 때, 먼저 선언된 슈퍼클래스의 메소드가 다른 슈퍼클래스들의 메소드를 덮어쓰게 되어, 같은 메소드가 선언되어 있다면 먼저 선언된 슈퍼클래스의 메소드가 호출됩니다.

>>> from diamond import *
>>> d = D()
>>> d.pong()
pong B: 
>>> C.pong(d)
PONG C: 
>>> print(D.__mro__)
(, , , , )

MRO의 예외: 네이티브 메서드 오버라이딩

C 언어로 구성된 네이티브 메서드는 서브클래스가 오버라이드한 메서드를 호출하지 않습니다.

>>> class DoppelDict(dict):
...     def __setitem__(self, key, value):
...             super().__setitem__(key, [value]*2)
...
>>> dd = DoppelDict(one=1)  # 오버라이드 무시
>>> dd
{'one': 1}
>>> dd['two'] = 2  # __setitem__ 호출
>>> dd
{'one': 1, 'two': [2, 2]}
>>> dd.update(three=3)  # 오버라이드 무시
>>> dd
{'one': 1, 'two': [2, 2], 'three': 3}

원하는 행동이 내장 자료형이 제공하는 기능과 상당히 다를 때는, 파이썬으로 구현된 collections.abc에서 제공하는 적절한 ABC를 상속해서 직접 구현하는 것이 좋습니다.

>>> import collections
>>> class DoppleDict2(collections.UserDict):
...     def __setitem__(self, key, value):
...             super().__setitem__(key, [value]*2)
...
>>> dd = DoppleDict2(one=1)
>>> dd
{'one': [1, 1]}
>>> dd['two'] = 2
>>> dd
{'one': [1, 1], 'two': [2, 2]}
>>> dd.update(three=3)

파이썬에서의 믹스인

믹스인은 여러 클래스의 일부분을 떼어서, 새로운 자료형을 정의하지 않고, 단지 다른 클래스에서 재사용할 속성이나 메소드를 묶어 놓은 클래스입니다.

__mro__ 속성을 보면 여러 슈퍼클래스를 다중 상속하는 경우, 선언문의 왼쪽에서부터 오른쪽으로 조회가 일어나는 것을 알 수 있습니다.

이런 방법으로 공통으로 쓰일 기능을 믹스인 클래스에 추가하여 코드 재사용성을 높일 수 있습니다.

Mixin 예시: DRF

Mixin을 적절하게 사용한 예는 Django REST Framework(DRF)에서 찾아볼 수 있습니다.

아래는 DRF의 ListModelMixin입니다.

queryset을 리스팅하는 기능을 제공하는 믹스인으로, GenericAPIView의 self.filter_queryset, self.get_queryset, self.get_serializer 등의 메소드를 활용해서 데이터베이스에 저장된 데이터들을 목록 형태로 response를 리턴해 줍니다.

만약 Mixin을 사용하지 않는다면, 해당 기능을 모든 view에 직접 반복적으로 구현해야 합니다. 하지만, Mixin을 사용한다면, 적절한 APIView를 상속하고 Mixin을 상속하기만 하면 해당 기능을 재사용할 수 있습니다.

다중 상속 다루는 방법

상속 구조가 뒤엉키는 것을 방지하기 위해서 다음 조언을 따르시면 좋습니다.

인터페이스 상속과 구현 상속을 구분한다.

ABC를 이용해서 인터페이스를 명확히 한다. - 파이썬에서는 인터페이스를 정의하기 위해서 abc.ABC나 다른 ABC를 상속하여, ABC임을 명시합니다.

코드를 재사용하기 위해 믹스인을 사용한다. - is-a 관계를 나타내지 않고 서로 관련 없는 여러 서브클래스에서 코드를 재사용하기 위한 클래스는 명시적으로 믹스인 클래스로 만들어야 합니다. - 이 때, 믹스인 클래스로 객체를 생성해서는 안되며, 믹스인 클래스를 상속하는 구상 클래스는 다른 클래스도 상속해야 합니다.

이름을 통해 믹스인임을 명확히 한다. - 파이썬에서는 믹스인을 정의하는 언어적 정의 방법이 없습니다. 따라서 클래스명 뒤에 Mixin을 붙일 것을 강력히 권장합니다.

ABC가 믹스인이 될 수는 있지만, 믹스인이라고 해서 ABC인 것은 아니다.

두 개 이상의 구상클래스에서 상속받지 않는다.

사용자에게 집합 클래스를 제공한다. - 집합클래스란 다수의 ABC 또는 Mixin을 통합하는 클래스입니다. ABC 또는 Mixin을 조합해서 호출 코드에 유용한 기능을 제공할 수 있을 때는, 이들을 적절히 통합하는 클래스를 제공하는 것이 좋습니다.

클래스 상속보다 객체 구성을 사용하라.

정리

이 번 포스트에서는 파이썬이 다중 상속을 다루는 방법을 배웠습니다. 다중 상속은 클래스 간의 관계를 복잡하게 만들기 때문에 JAVA와 같은 언어에서는 언어 차원에서 지원하지 않지만, 잘 사용한다면 코드 재사용성을 높일 수 있습니다.

fluent python의 테크니컬 리뷰어 레나르트 레제브로의 코멘트로 포스트를 마치겠습니다.

다중 상속의 위험과 유해성이 지나치게 확대 포장되어 있다. 사실 나는 다중 상속과 관련해서 그다지 큰 문제를 겪지 않았다.

행복한 개발 되세요~ 감사합니다.

이 포스트는 루시아누 하말류의 책 [Fluent Python]을 참고해서 작성했습니다.

Get to know us better! Join our official channels below.

Telegram(EN) : t.me/Humanscape KakaoTalk(KR) : open.kakao.com/o/gqbUQEM Website : humanscape.io Medium : medium.com/humanscape-ico Facebook : www.facebook.com/humanscape Twitter : twitter.com/Humanscape_io Reddit : https://www.reddit.com/r/Humanscape_official Bitcointalk announcement : https://bit.ly/2rVsP4T Email : support@humanscape.io

기업문화 엿볼 때, 더팀스

로그인

/