클래스 상속은 기존 클래스의 기능을 유지한 채로 새로운 기능을 추가할 때 사용합니다. 기능을 물려주는 쪽을 기반 클래스(Base Class), 상속받아 새롭게 만드는 쪽을 파생 클래스(Derived Class) 라고 합니다.
| 용어 | 다른 표현 |
|---|---|
| 기반 클래스 | 부모 클래스, 슈퍼 클래스 |
| 파생 클래스 | 자식 클래스, 서브 클래스 |
상속을 사용하면 기존 코드를 재사용할 수 있어 효율적입니다. 단, 상속이 항상 정답은 아닙니다. 같은 종류의 동등한 관계일 때는 상속을, 그 외의 경우에는 속성에 인스턴스를 넣는 포함(Composition) 방식을 사용하는 것이 적합합니다.
클래스를 만들 때 괄호 안에 기반 클래스 이름을 넣으면 됩니다.
# 기반 클래스
class Animal:
def __init__(self, name):
self.name = name
def breathe(self):
print(f"{self.name}이(가) 숨을 쉽니다.")
# 파생 클래스: Animal을 상속
class Dog(Animal):
def bark(self):
print(f"{self.name}이(가) 짖습니다. 멍멍!")
# 파생 클래스는 기반 클래스의 기능을 모두 사용 가능
dog = Dog("바둑이")
dog.breathe() # 바둑이이(가) 숨을 쉽니다.
dog.bark() # 바둑이이(가) 짖습니다. 멍멍!
파생 클래스에서 __init__을 생략하면 기반 클래스의 __init__이 자동으로 호출됩니다. 파생 클래스에서 __init__을 새로 정의할 때는 super()로 기반 클래스의 __init__을 호출해줍니다.
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 기반 클래스 __init__ 호출
self.breed = breed # 파생 클래스만의 속성 추가
def bark(self):
print(f"{self.name}({self.breed})이(가) 짖습니다. 멍멍!")
dog = Dog("바둑이", "진돗개")
dog.bark() # 바둑이(진돗개)이(가) 짖습니다. 멍멍!
메서드 오버라이딩(Method Overriding) 은 파생 클래스에서 기반 클래스의 메서드를 새로 정의하는 것입니다. 오버라이딩(Overriding)은 "무시하다, 우선하다"는 뜻으로, 기반 클래스의 메서드를 무시하고 새로운 동작을 정의한다는 의미입니다.
class Animal:
def speak(self):
print("...")
class Dog(Animal):
def speak(self): # 오버라이딩: 기반 클래스의 speak()를 재정의
print("멍멍!")
class Cat(Animal):
def speak(self): # 오버라이딩
print("야옹!")
dog = Dog()
cat = Cat()
dog.speak() # 멍멍!
cat.speak() # 야옹!
기반 클래스의 메서드를 완전히 대체하지 않고 재활용하면서 기능을 추가하고 싶을 때는 super()로 기반 클래스의 메서드를 호출합니다. 중복 코드를 줄일 수 있습니다.
class Animal:
def speak(self):
print("동물이 소리를 냅니다.")
class Dog(Animal):
def speak(self):
super().speak() # 기반 클래스 메서드 재활용
print("멍멍!") # 추가 동작
dog = Dog()
dog.speak()
# 동물이 소리를 냅니다.
# 멍멍!
다중 상속(Multiple Inheritance) 은 여러 기반 클래스로부터 동시에 상속받는 방법입니다. 괄호 안에 클래스 이름을 콤마로 구분해서 넣으면 됩니다.
class Flyable:
def fly(self):
print("날아갑니다!")
class Swimmable:
def swim(self):
print("헤엄칩니다!")
# 두 클래스를 동시에 상속
class Duck(Flyable, Swimmable):
def quack(self):
print("꽥꽥!")
duck = Duck()
duck.fly() # 날아갑니다!
duck.swim() # 헤엄칩니다!
duck.quack() # 꽥꽥!
다중 상속에서 같은 이름의 메서드가 여러 기반 클래스에 존재할 경우, 메서드 탐색 순서(MRO, Method Resolution Order) 에 따라 어떤 메서드가 호출될지 결정됩니다. 클래스.mro()로 탐색 순서를 확인할 수 있습니다.
print(Duck.mro())
# [<class 'Duck'>, <class 'Flyable'>, <class 'Swimmable'>, <class 'object'>]
추상 클래스(Abstract Class) 는 메서드의 목록만 정의하고, 실제 구현은 파생 클래스에 맡기는 클래스입니다. 상속받는 클래스에서 특정 메서드를 반드시 구현하도록 강제할 때 사용합니다.
추상 클래스를 만들려면 abc 모듈을 가져와야 합니다.
from abc import ABCMeta, abstractmethod
# 추상 클래스 선언
class Shape(metaclass=ABCMeta):
@abstractmethod
def area(self): # 추상 메서드: 구현 강제
pass
@abstractmethod
def perimeter(self): # 추상 메서드: 구현 강제
pass
추상 클래스를 상속받으면 @abstractmethod가 붙은 모든 추상 메서드를 반드시 구현해야 합니다. 하나라도 빠지면 인스턴스를 생성할 수 없습니다.
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self): # 추상 메서드 구현
return 3.14 * self.radius ** 2
def perimeter(self): # 추상 메서드 구현
return 2 * 3.14 * self.radius
c = Circle(5)
print(c.area()) # 78.5
print(c.perimeter()) # 31.400000000000002
# 추상 클래스는 인스턴스 생성 불가
# s = Shape() # TypeError: Can't instantiate abstract class
| 개념 | 설명 | 핵심 문법 |
|---|---|---|
| 상속 | 기반 클래스 기능을 물려받음 | class Dog(Animal): |
super() | 기반 클래스의 메서드 호출 | super().__init__() |
| 오버라이딩 | 기반 클래스 메서드를 재정의 | 같은 이름으로 메서드 작성 |
| 다중 상속 | 여러 클래스를 동시에 상속 | class Duck(Flyable, Swimmable): |
| MRO | 메서드 탐색 순서 확인 | 클래스.mro() |
| 추상 클래스 | 메서드 구현을 강제하는 클래스 | @abstractmethod |