다른 클래스에 포함되는 하위 클래스를 정의하는 방법과 클래스의 포함 관계에서 나타나는 특징을 알아본다.
아래와 같이 ‘케익’ 클래스를 상위 클래스로 삼는 ‘초콜릿 케익’ 클래스 정의할 수 있다.
# 상위 클래스
class Cake:
"""케익을 나타내는 클래스"""
coat = '생크림'
def __init__(self, topping, price, candles=0):
"""인스턴스를 초기화한다."""
self.topping = topping # 케익에 올린 토핑
self.price = price # 케익의 가격
self.candles = candles # 케익에 꽂은 초 개수
def describe(self):
"""이 케익에 관한 정보를 화면에 출력한다."""
print('이 케익은', self.coat, '으로 덮여 있다.')
print(self.topping, '을 올려 장식했다.')
print('가격은', self.price, '원이다.')
print('초가', self.candles, '개 꽂혀 있다.')
# 하위 클래스
class ChocolateCake(Cake):
"""초콜릿 케익을 나타내는 클래스."""
coat = '초콜릿'
cacao_percent = 32.0
❶ 상위 클래스에 정의된 속성은 하위 클래스에서 고쳐 정의할 수 있다. 이를 재정의(override)라고 한다.
❷ 하위 클래스에서 추가한 속성은 하위 클래스에서 읽을 수 있다.
❸ 재정의하지 않은 속성과 메서드는 그대로 사용할 수 있다. chocolate_cake_1 인스턴스를 만들고 사용할 때, 상위 클래스 Cake의 __init__() 메서드와 describe() 메서드를 그대로 사용했다.
print(ChocolateCake.coat) # ❶ 재정의한 클래스 속성
print(ChocolateCake.cacao_percent) # ❷ 추가한 클래스 속성
# ❸ 상위 클래스 Cake의 __init__() 메서드와 __describe__() 메서드 이용하기
chocolate_cake_1 = ChocolateCake('이슬', 12000)
chocolate_cake_1.describe()
초콜릿
32.0
이 케익은 초콜릿 으로 덮여 있다.
이슬 을 올려 장식했다.
가격은 12000 원이다.
초가 0 개 꽂혀 있다.
어떤 클래스의 하위 클래스를 정의하는 것을 ‘상속(inherit)’ 또는 ‘확장(extend)’이라고 부르기도 한다.
하위 클래스가 상위 클래스의 속성을 공유하는 것이 마치 자식이 부모의 특성을 대물림하는 것과 비슷하기 ‘상속’이라는 용어가 붙었다. 하위 클래스는 상위 클래스의 모든 속성을 고스란히 가지면서도 속성을 추가로 더 가질 수 있기 때문에 상위 클래스를 ‘확장’한다고 부르기도 한다.
Cake 클래스의 또 다른 하위 객체로, 아이스크림 케익을 나타내는 클래스 IceCreamCake을 정의해 보자. __init__() 메서드도 재정의하자.
class IceCreamCake(Cake):
"""아이스크림 케익을 나타내는 클래스."""
coat = '아이스크림'
flavor = '정해지지 않은 맛'
def __init__(self, flavor, topping, price, candles=0):
"""인스턴스를 초기화한다."""
self.flavor = flavor # 아이스크림의 맛
super().__init__(topping, price, candles)
이 메서드는 전달받은 인자 중에서 flavor만 인스턴스 초기화에 사용하고, 나머지 인자는 직접 사용하지 않는다. 대신, 상위 클래스인 Cake 클래스의 __init__() 메서드에 남은 인자를 전달해 초기화하도록 하였다.
여기서 사용한 super()는 이 인스턴스가 속한 클래스의 상위 클래스로 평가되는 함수로, 실행하면 Cake 클래스가 된다. 이 메서드에서 인스턴스의 모든 속성을 직접 초기화할 수도 있지만, 코드가 중복되는 것을 방지하고 상위 클래스의 동작을 그대로 따를 수 있으므로 이 방식이 더 유리하다.
하위 클래스에서 상위 클래스를 가리킬 때 super() 함수를 사용할 수 있다.
ice_cream_cake_1 = IceCreamCake('바닐라맛', '쿠키 인형', 12000)
ice_cream_cake_1.describe()
이 케익은 아이스크림 으로 덮여 있다.
쿠키 인형 을 올려 장식했다.
가격은 12000 원이다.
초가 0 개 꽂혀 있다.
issubclass() 함수를 사용하면 어떤 클래스가 다른 클래스의 하위 클래스인지를 검사할 수 있다.
>>> issubclass(ChocolateCake, Cake) # ChocolateCake이 Cake의 하위 클래스인가?
True
모든 클래스는 object의 하위 클래스다.
하위 클래스는 상위 클래스의 이름공간을 공유하는 동시에, 자신만의 이름공간도 갖는다. 반면 상위 클래스는 하위 클래스의 이름공간에 접근하지 못한다.
상위 클래스와 하위 클래스의 이름공간의 관계는 클래스와 인스턴스의 이름공간의 관계와 유사하다. 아래 그림과 같이, 하위클래스와 하위 클래스의 인스턴스에서는 상위 클래스의 이름공간에 접근할 수 있다. 하지만 안쪽의 상위 클래스에서는 바깥쪽의 하위 클래스의 이름공간에 접근하지 못한다.
하위 클래스에서 속성을 읽을 때, 먼저 자신의 이름공간에서 찾고 없으면 상위 클래스에서 찾는다.
상위 클래스가 여러 개인 경우 왼쪽에 나열한 클래스의 이름공간을 먼저 검색한다.
클래스 중에는 멤버 변수 없이 모든 속성이 메서드로만 이루어진 특별한 클래스가 있는데, 믹스인(mixin)이라고 부른다. 믹스인에는 변화하는 상태가 존재하지 않고 오직 메서드만 있기 때문에 다중 상속을 하더라도 비교적 안전하다.
본 포스팅은 아래의 사이트를 참고하여 작성되었습니다.
연오의 파이썬 https://python.bakyeono.net/chapter-6.html
코딩도장 https://dojang.io/