
27. 클래스와 객체 (2) - 상속과 다형성
기존 클래스를 확장해서 새 클래스를 만들 수 있다면? 코드 재사용의 끝판왕, 상속(Inheritance)과 다형성(Polymorphism)을 알아보자. 객체지향의 진짜 힘은 여기서 나온다.
상속이란?
상속은 기존 클래스(부모)의 속성과 메서드를 물려받아 새 클래스(자식)를 만드는 것이다.
class Animal: # 부모 클래스 (기반 클래스)
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name}이(가) 소리를 냅니다.")
class Dog(Animal): # 자식 클래스 (Animal을 상속)
def speak(self):
print(f"{self.name}: 멍멍!")
class Cat(Animal): # 자식 클래스 (Animal을 상속)
def speak(self):
print(f"{self.name}: 야옹!")
dog = Dog("멍멍이")
cat = Cat("나비")
dog.speak() # 멍멍이: 멍멍!
cat.speak() # 나비: 야옹!
Dog과 Cat은 Animal로부터 name 속성과 __init__ 메서드를 물려받았다. speak() 메서드만 각자 다르게 정의했다.

상속의 기본 문법
class 부모클래스:
# 부모의 속성과 메서드
class 자식클래스(부모클래스): # 괄호 안에 부모 지정
# 자식만의 속성과 메서드 추가
자식 클래스에 기능 추가
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
print(f"{self.name} ({self.age}살)")
class Dog(Animal):
def fetch(self): # Dog만의 메서드
print(f"{self.name}이(가) 공을 물어옵니다!")
class Cat(Animal):
def purr(self): # Cat만의 메서드
print(f"{self.name}이(가) 골골거립니다~")
dog = Dog("멍멍이", 3)
dog.info() # 멍멍이 (3살) — 부모의 메서드 사용
dog.fetch() # 멍멍이이(가) 공을 물어옵니다!
cat = Cat("나비", 2)
cat.info() # 나비 (2살) — 부모의 메서드 사용
cat.purr() # 나비이(가) 골골거립니다~
super() - 부모의 메서드 호출
자식 클래스에서 __init__을 재정의할 때, 부모의 초기화도 실행해야 한다. super()를 쓴다.
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
class Dog(Animal):
def __init__(self, name, age, breed):
super().__init__(name, age) # 부모의 __init__ 호출
self.breed = breed # Dog만의 속성 추가
def info(self):
print(f"{self.name} ({self.breed}, {self.age}살)")
dog = Dog("멍멍이", 3, "골든리트리버")
dog.info() # 멍멍이 (골든리트리버, 3살)
super()를 안 쓰면?
class Dog(Animal):
def __init__(self, name, age, breed):
# super().__init__(name, age)를 빼먹으면...
self.breed = breed
dog = Dog("멍멍이", 3, "골든리트리버")
print(dog.breed) # 골든리트리버
print(dog.name) # AttributeError! name 속성이 없다!
super().__init__()을 호출하지 않으면 부모의 초기화가 실행되지 않아 부모의 속성이 생성되지 않는다.
메서드 오버라이딩 (Override)
부모의 메서드를 자식에서 같은 이름으로 재정의하는 것을 오버라이딩이라 한다.
class Shape:
def __init__(self, color="검정"):
self.color = color
def area(self):
return 0
def describe(self):
print(f"{self.color} 도형, 넓이: {self.area()}")
class Circle(Shape):
def __init__(self, radius, color="빨강"):
super().__init__(color)
self.radius = radius
def area(self): # 오버라이딩
return 3.14159 * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width, height, color="파랑"):
super().__init__(color)
self.width = width
self.height = height
def area(self): # 오버라이딩
return self.width * self.height
# 사용
c = Circle(5)
r = Rectangle(4, 6)
c.describe() # 빨강 도형, 넓이: 78.53975
r.describe() # 파랑 도형, 넓이: 24
describe() 메서드는 부모에 한 번만 정의했지만, area()가 각 자식에서 오버라이딩 되어 다르게 동작한다.
다형성 (Polymorphism)
다형성은 같은 메서드 이름이 객체에 따라 다르게 동작하는 것이다.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass
class Dog(Animal):
def speak(self):
return f"{self.name}: 멍멍!"
class Cat(Animal):
def speak(self):
return f"{self.name}: 야옹!"
class Duck(Animal):
def speak(self):
return f"{self.name}: 꽥꽥!"
# 다형성의 힘: 같은 코드로 다른 결과
animals = [Dog("멍멍이"), Cat("나비"), Duck("도날드")]
for animal in animals:
print(animal.speak()) # 각 동물에 맞는 소리!
출력:
멍멍이: 멍멍!
나비: 야옹!
도날드: 꽥꽥!
핵심은 animal.speak()라는 동일한 코드가 객체의 타입에 따라 다르게 동작한다는 것이다.
isinstance() - 타입 확인
isinstance()로 객체가 특정 클래스의 인스턴스인지 확인할 수 있다.
dog = Dog("멍멍이")
cat = Cat("나비")
print(isinstance(dog, Dog)) # True
print(isinstance(dog, Animal)) # True (부모 클래스도 True!)
print(isinstance(dog, Cat)) # False
print(isinstance(cat, Cat)) # True
print(isinstance(cat, Animal)) # True
실전 활용
def animal_hospital(animal):
"""동물 병원 접수"""
if isinstance(animal, Dog):
print(f"강아지 {animal.name} 접수 완료 (외과)")
elif isinstance(animal, Cat):
print(f"고양이 {animal.name} 접수 완료 (내과)")
else:
print(f"{animal.name} 접수 완료 (일반)")
animal_hospital(Dog("멍멍이")) # 강아지 멍멍이 접수 완료 (외과)
animal_hospital(Cat("나비")) # 고양이 나비 접수 완료 (내과)
다중 상속
파이썬은 여러 부모 클래스를 동시에 상속받을 수 있다. 하지만 복잡해지기 쉬우므로 주의가 필요하다.
class Flyable:
def fly(self):
print(f"{self.name}이(가) 날아갑니다!")
class Swimmable:
def swim(self):
print(f"{self.name}이(가) 수영합니다!")
class Duck(Animal, Flyable, Swimmable):
def speak(self):
return f"{self.name}: 꽥꽥!"
duck = Duck("도날드")
print(duck.speak()) # 도날드: 꽥꽥!
duck.fly() # 도날드이(가) 날아갑니다!
duck.swim() # 도날드이(가) 수영합니다!
MRO (Method Resolution Order)
다중 상속에서 메서드 검색 순서를 확인할 수 있다.
print(Duck.__mro__)
# (<class 'Duck'>, <class 'Animal'>, <class 'Flyable'>, <class 'Swimmable'>, <class 'object'>)
다중 상속은 강력하지만, 초보 단계에서는 단일 상속을 주로 사용하고, 다중 상속은 Mixin 패턴 정도로만 활용하는 것을 권장한다.
실전 예시: 직원 관리 시스템
class Employee:
"""기본 직원"""
def __init__(self, name, employee_id, salary):
self.name = name
self.employee_id = employee_id
self.salary = salary
def get_pay(self):
return self.salary
def info(self):
print(f"[{self.employee_id}] {self.name} - 급여: {self.get_pay():,}원")
class Manager(Employee):
"""관리자 — 기본급 + 보너스"""
def __init__(self, name, employee_id, salary, bonus):
super().__init__(name, employee_id, salary)
self.bonus = bonus
def get_pay(self): # 오버라이딩
return self.salary + self.bonus
class Intern(Employee):
"""인턴 — 기본급의 50%"""
def get_pay(self): # 오버라이딩
return int(self.salary * 0.5)
# 직원 목록
employees = [
Employee("김사원", "E001", 3000000),
Manager("이부장", "M001", 5000000, 2000000),
Intern("박인턴", "I001", 2400000),
]
print("=== 급여 명세서 ===")
total = 0
for emp in employees:
emp.info()
total += emp.get_pay()
print(f"\n총 급여 합계: {total:,}원")
출력:
=== 급여 명세서 ===
[E001] 김사원 - 급여: 3,000,000원
[M001] 이부장 - 급여: 7,000,000원
[I001] 박인턴 - 급여: 1,200,000원
총 급여 합계: 11,200,000원
직접 해보기
문제 1. Vehicle 부모 클래스와 Car, Bicycle 자식 클래스를 만들어보자. Vehicle은 name과 speed 속성을 가지고, 각 자식은 describe() 메서드를 오버라이딩한다.
문제 2. Shape → Circle, Rectangle, Triangle 상속 구조를 만들고, 각 도형의 넓이를 계산하는 area() 메서드를 오버라이딩해보자.
문제 3. 게임 캐릭터 시스템을 만들어보자. Character 부모 클래스(이름, HP)와 Warrior(공격력), Mage(마법력) 자식 클래스를 만들자. 각 클래스에 attack() 메서드를 다르게 구현하자.
문제 4. 동물 리스트를 만들고, for문으로 순회하면서 isinstance()를 사용해 동물별로 다른 메시지를 출력해보자.
정답 보기
# 문제 1 - Vehicle 상속
class Vehicle:
def __init__(self, name, speed):
self.name = name
self.speed = speed
def describe(self):
print(f"{self.name} (최대 {self.speed}km/h)")
class Car(Vehicle):
def __init__(self, name, speed, fuel):
super().__init__(name, speed)
self.fuel = fuel
def describe(self):
print(f"자동차: {self.name} ({self.fuel}, {self.speed}km/h)")
class Bicycle(Vehicle):
def describe(self):
print(f"자전거: {self.name} ({self.speed}km/h)")
car = Car("소나타", 200, "가솔린")
bike = Bicycle("삼천리", 30)
car.describe() # 자동차: 소나타 (가솔린, 200km/h)
bike.describe() # 자전거: 삼천리 (30km/h)
# 문제 2 - Shape 상속
class Shape:
def area(self):
return 0
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Triangle(Shape):
def __init__(self, base, height):
self.base = base
self.height = height
def area(self):
return self.base * self.height / 2
shapes = [Circle(5), Rectangle(4, 6), Triangle(3, 8)]
for s in shapes:
print(f"{s.__class__.__name__}: {s.area():.2f}")
# 문제 3 - 게임 캐릭터
class Character:
def __init__(self, name, hp):
self.name = name
self.hp = hp
def attack(self):
print(f"{self.name}이(가) 공격합니다!")
class Warrior(Character):
def __init__(self, name, hp, power):
super().__init__(name, hp)
self.power = power
def attack(self):
print(f"[전사] {self.name}: 검으로 {self.power} 데미지!")
class Mage(Character):
def __init__(self, name, hp, magic):
super().__init__(name, hp)
self.magic = magic
def attack(self):
print(f"[마법사] {self.name}: 마법으로 {self.magic} 데미지!")
party = [Warrior("아서", 100, 25), Mage("멀린", 70, 40)]
for c in party:
c.attack()
# 문제 4 - isinstance 활용
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal): pass
class Cat(Animal): pass
class Bird(Animal): pass
animals = [Dog("멍멍이"), Cat("나비"), Bird("짹짹이"), Dog("바둑이")]
for a in animals:
if isinstance(a, Dog):
print(f"{a.name}: 산책 가자!")
elif isinstance(a, Cat):
print(f"{a.name}: 츄르 줄까?")
elif isinstance(a, Bird):
print(f"{a.name}: 하늘 높이 날아라!")
오늘의 정리
| 항목 | 내용 |
|---|---|
| 상속 | class 자식(부모): — 부모의 속성·메서드를 물려받음 |
| super() | 부모의 메서드를 호출. super().__init__()으로 부모 초기화 |
| 오버라이딩 | 부모의 메서드를 자식에서 같은 이름으로 재정의 |
| 다형성 | 같은 메서드가 객체 타입에 따라 다르게 동작 |
| isinstance() | 객체의 타입 확인. 부모 클래스도 True |
| 다중 상속 | class 자식(부모1, 부모2): — 여러 부모 상속 가능 |
다음 편 예고: 매직 메서드 - 파이썬 객체의 비밀
__str__, __len__, __add__... 언더스코어 두 개로 감싸인 특별한 메서드들의 비밀을 파헤쳐보자!
태그: 파이썬 Python 파이썬독학 상속 inheritance 다형성 오버라이딩 super OOP 파이썬중급 IT교육
'Python' 카테고리의 다른 글
| 웹 스크래핑 - 인터넷에서 데이터 수집하기 (0) | 2026.02.23 |
|---|---|
| 28. 매직 메서드 - 파이썬 객체의 비밀 (0) | 2026.02.23 |
| 26. 클래스와 객체 (1) - 나만의 자료형 만들기 (0) | 2026.02.22 |
| 25. 가상환경과 프로젝트 관리 - 프로처럼 세팅하기 (1) | 2026.02.22 |
| 24. 정규표현식 - 문자열 처리의 끝판왕 (0) | 2026.02.22 |
댓글