객체지향 프로그래밍에서 클래스를 정의하는 중요하게 적용되는 특징 4가지가 있다. 캡슐화(Encapsulation), 정보은닉(Information Hiding), 상속(Inheritance), 그리고 다형성(Polymorphism) 이다. 이번 장에서는 마지막 네번째 특징인 다형성의 개념을 살펴본다.
다형성(Polymorphism)의 개념
Polymorphism 이라는 단어는 그리스어에서 유래한 단어로 Poly(‘많음’, '여러가지’ 의미), Morph("형태, 모습, Form, Shape), ~ism("특징, 상태,Condition, State) 의 합성어이다. 즉, 여러 가지 형태를 가질 수 있는 특징, 상태 를 의미한다.
일반적인 객체지향 프로그래밍에서는 다형성을 변수(참조 변수)의 타입과, 실제 메모리에 생성되어 바인딩된 객체의 타입이 서로 다를 수 있다는 관점에서 설명하고, 프로그래밍 언어에서 이를 구현하기 위한 장치들을 설명한다. 그러나 파이썬에서의 다형성(Polymorphism)은 변수의 타입을 엄격히 정의하지 않고, 객체가 실제로 수행할 수 있는 동작을 중요하게 보기 때문에, 다형성은 메서드 오버라이딩, 덕 타이핑, 연산자 오버로딩을 통해 이해할 수 있다.
1. 메소드 오버라이딩(Method Overriding) - 하나의 메서드 이름이 다양한 자식 클래스의 다양한 구현, 실행시 한 변수에 다양한 자식 클래스 인스턴스가 대입되면서 매서드의 다형성이 나타난다.
2. Duck Type - 상속관계가 아니어도 필요한 메서드나 속성을 가지고 있으면 같은 방식으로 사용할 수 있다.
3. 오퍼레이터 오버로딩(Operator Overloading) - 일반적인 오퍼레이터(+,-,*,/)의 의미가 적용되는 클래스에 따라 재정의된다.
1. 메소드 오버라이딩(Method Overriding)
상속 구조에서 특정 메소드의 내부구현(로직)이 Polymorphic 하게 수행되는 것을 의미한다. 예를 들어 Mammal 클래스의 speak()을 Cat 클래스와 Dog 클래스에서 상속받아 각자의 특성에 맞게 재정의한 후, 호출 할 수 있다.

이와 같은 메소드 오버라이딩을 코드로 구현하면 아래와 같은데, 여기서 집중해야 할 곳은 13라인 부터이다.
class Mammal:
def speak(self): # 부모 클래스의 메서드
print("It makes its own sound.")
class Dog(Mammal):
def speak(self): # 메서드 오버라이딩
print("Woof!")
class Cat(Mammal):
def speak(self): # 메서드 오버라이딩
print("Meow!")
animalList = [Dog(), Cat(), Mammal()]
for animal in animalList:
animal.speak()
- 13라인 - animalList라는 리스트 타입의 변수에 Dog(), Cat(), Mammal() 클래스의 인스턴스를 각각 하나씩 생성하여 넣어준다.
- 14라인 - animalList의 인스턴스들을 animal 변수에 하나씩 대입해서 for문을 실행한다.
- 15라인 animal.speak()을 호출한다. 이때, animal 변수에 대입된 인스턴스의 타입에 따라 Dog.speak()이 실행된지, Cat.speak()이 실행될지, Mammal.speak()이 실행될지가 결정된다. 따라서 anmial.speak()은 polymorphic하다.
2. 덕타입핑(Duck Typing)
파이썬에서 Duck Typing(덕 타이핑)은 객체의 클래스나 상속 관계보다, “그 객체가 필요한 메서드나 기능을 가지고 있는가?”를 기준으로 사용하는 방식이다.
“오리처럼 걷고, 오리처럼 소리내면 오리로 본다.”
→ 객체의 실제 타입보다, 필요한 동작을 할 수 있으면 같은 종류로 취급한다.
클래스 다이어그램으로 표현하면 아래와 같은데, 아래의 각 클래스들은 speak() 메소드를 모두 가지고 있다. Mammal에서 상속받은 Cat, Dog 클래스의 speak()은 재정이 되어도, speak()으로 호출되고 인스턴스에 따라 서로 다른 speak()이 실행된다는 것은 상속관계와 오버라이딩으로 통한 다형성으로 이해될 수 있다. 그런데 덕타이핑은 Robot 클래스와 같이 상속관계로 연관되지 않은 클래스도 speak()이라는 메소드가 있으면 이를 하나의 변수에 대응되어 호출 될 수 있음을 의미한다.

위 클래스 다이어그램을 파이썬 코드로 구현하면 아래와 같다.
class Mammal:
def speak(self):
print("It makes its own sound.")
class Dog(Mammal):
def speak(self):
print("Woof!")
class Cat(Mammal):
def speak(self):
print("Meow!")
class Robot:
def speak(self):
print("Beep! Hello.")
def make_sound(speaker):
speaker.speak()
dog = Dog()
cat = Cat()
robot = Robot()
make_sound(dog)
make_sound(cat)
make_sound(robot)
- 1~15라인: 클래스 다이어그램에 정의된 클래스들의 구현이다.
- 17-18라인: speaker를 파라메터로 받는 make_sound() 함수로 speaker.speak()을 호출한다. 여기서 다형성이 이루어 지는데, speaker 변수에 어떤 타입의 인스턴스가 대입되던지, speak()이라는 메소드가 있으면 이를 수행해 줄 수 있다.
- 20~22라인: 각 클래스의 인스턴스를 생성한다.
- 24~26라인: Mammal과 상속관계에 잇는 Dog, Cat 클래스의 인스턴스와 전혀 상관없는 Robot 클래스의 인스턴스를 make_sound()함수의 인자로 넘겨준다. 세 인스턴스의 speak()함수가 호출될 것을 예상한다.
3. 연산자 오버로딩(Operator Overloading)
1 + 2 = 3 이다. + 연산자는 +연산자 앞의 수와 뒤의 수 값을 더하는 기능이다. 이러한 수에서의 일반적인 기능을 사용자가 정의한 클래스에 적용한다면 어떻게 될까? Dog 클래스의 인스턴스 dog과 Cat 클래스의 인스턴스 cat을 더한다면?
dog + cat = ?
이렇게 일반적인 연산자(+, -, , == 등)를 클래스에서 적용되도록 재정의 하는것을 연산자 오버로딩(Operator Overloading)이라 한다. 아래 코드는 Mammal 이라는 클래스에 연산자 오버로딩을 구현한 예 이다. class Mammal:
def __init__(self, name, age):
self.name = name
self.age = age
def __add__(self, other): # 나이 합치기
return Mammal(self.name + "-" + other.name, self.age + other.age)
def __sub__(self, other): # 나이 차이 빼기
return Mammal(self.name + "-분리", abs(self.age - other.age))
def __mul__(self, num): # 나이에 수 곱하기
return Mammal(self.name, self.age * num)
def __str__(self):
return f"{self.name} (나이: {self.age})"
dog = Mammal("바둑이", 5)
cat = Mammal("나비", 3)
print(dog + cat) # "바둑이-나비 (나이: 8)"
print(dog - cat) # "바둑이-분리 (나이: 2)"
print(dog * 2) # "바둑이 (나이: 10)"
재정의한 연산자는 +, -, x 이다. Mammal 클래스에서 +, -, x는 이름은 연결하고, 나이를 +, -, x 한 새로운 Mammal 인스턴스를 생성하는 것으로 정의하였다.
연산자를 재정의하기 위해서 +는 __add__() 함수로, -는 __sub__() 함수로, x는 __mul__()함수로 구현한다. (파이썬 규칙)
- 6라인: + 연산자를 구현한다. 두 인스턴스의 이름을 연결하고, 나이도 합친다.
- 9라인: - 연산자를 구현한다. 두 인스턴스의 이름을 연결하고, 나이는 차를 구한다.
- 12라인: x 연산자를 구현한다. 두 인스턴스의 이름을 연결하고, 나이는 곱해준다.
- 15라인: Mammal 클래스의 인스턴스를 print()하거나 str()으로 표현할때 호출 되는 함수를 재정의 한다.
- __str__()은 객체를 문자열로 표현할 때 자동으로 호출되는 특별 메서드(special method)이다.
- 객체를 print() 하거나 str()로 변환하면, 파이썬은 내부적으로 __str__()을 호출된다.
- Mammal 클래스의 인스턴스를 print()하거나 str()으로 표현할때 호출 되는 함수를 재정의 한 것이다. (오퍼레이터 오버로딩으로 부르지는 않음)
- 21~23라인
- dog + cat, dog - cat, dog * 2를 수행하면 재정의된 __add__(), __sub__(), __mul__()이 호출되어 새로 생성된 Mammal클래스 타입의 인스턴스가 리턴된다.
- 리턴된 인스턴스를 print()하면 __str__()이 호출되어 name과 age가 string으로 반환되어 출력된다.
'[2] 객체지향 프로그래밍 설계(파이썬) > 6주차. 다형성' 카테고리의 다른 글
| 6.3 [실습] 클래스 수준의 리스트 속성 정의를 통한 다형성 연습 (0) | 2026.04.10 |
|---|---|
| 6.2 [추가] 클래스 정의 추가 사항 - 클래스 수준 속성 (0) | 2026.04.08 |