[Python 100일 챌린지] Day 38 - 다형성(Polymorphism)
강아지, 고양이, 새를 리스트에 넣고 각자 소리 내게 하려면 타입마다 다른 코드를 작성해야 할까요? 🤔 ──아니요! 모두
speak()메서드만 있다면 같은 방식으로 호출할 수 있습니다! 😊리모컨을 생각해보세요. TV, 에어컨, 선풍기 모두 “전원” 버튼이 있습니다. 리모컨마다 작동 원리는 다르지만, 우리는 그냥 “전원” 버튼만 누르면 되죠. 각 기기가 어떻게 켜지는지 신경 쓸 필요 없습니다!
게임 캐릭터도 마찬가지입니다. 전사, 마법사, 궁수 모두
attack()메서드가 있으면,for character in characters: character.attack()이렇게 간단하게 처리됩니다.이것이 다형성(Polymorphism)입니다! 하나의 인터페이스로 다양한 타입을 다루는 OOP의 꽃! 💡
🎯 오늘의 학습 목표
⭐⭐⭐⭐ (35-45분 완독)
📚 사전 지식
🎯 학습 목표 1: 다형성의 개념 이해하기
다형성(Polymorphism)은 “여러 형태”를 의미하며, 같은 인터페이스로 다양한 타입을 다루는 능력입니다.
🎸 실생활 비유: 악기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Guitar:
def play(self):
return "통통통♪"
class Piano:
def play(self):
return "딩동댕♬"
class Drum:
def play(self):
return "쿵짝쿵짝♩"
# 다형성 - 같은 메서드명, 다른 동작
instruments = [Guitar(), Piano(), Drum()]
for instrument in instruments:
print(instrument.play()) # 각자 다른 소리!
실행 결과:
1
2
3
통통통♪
딩동댕♬
쿵짝쿵짝♩
🎯 학습 목표 2: 덕 타이핑 이해하기 (Duck Typing)
“오리처럼 걷고, 오리처럼 꽥꽥거리면, 그것은 오리다”
Python은 타입보다 동작을 중요하게 여깁니다!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Dog:
def speak(self):
return "멍멍!"
class Cat:
def speak(self):
return "야옹~"
class Robot:
def speak(self):
return "삐빕삐빕"
def make_sound(obj):
"""객체의 타입에 관계없이 speak() 호출"""
return obj.speak()
# 모두 speak()만 있으면 OK!
print(make_sound(Dog())) # 멍멍!
print(make_sound(Cat())) # 야옹~
print(make_sound(Robot())) # 삐빕삐빕
isinstance() 체크 없이 작동
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class FileWriter:
def write(self, data):
with open('file.txt', 'w') as f:
f.write(data)
class DatabaseWriter:
def write(self, data):
# DB에 저장
print(f"DB에 저장: {data}")
class LogWriter:
def write(self, data):
# 로그 기록
print(f"로그: {data}")
def save_data(writer, data):
"""writer 타입 체크 없이 write() 호출"""
writer.write(data)
# 모든 writer는 write() 메서드만 있으면 됨
save_data(DatabaseWriter(), "User Data")
save_data(LogWriter(), "Error occurred")
🎯 학습 목표 3: 연산자 오버로딩 익히기
특수 메서드로 연산자 동작을 정의할 수 있습니다!
산술 연산자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
"""+ 연산자"""
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other):
"""- 연산자"""
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar):
"""* 연산자 (스칼라 곱)"""
return Vector(self.x * scalar, self.y * scalar)
def __str__(self):
return f"Vector({self.x}, {self.y})"
# 사용
v1 = Vector(3, 4)
v2 = Vector(1, 2)
v3 = v1 + v2 # __add__ 호출
print(v3) # Vector(4, 6)
v4 = v1 - v2 # __sub__ 호출
print(v4) # Vector(2, 2)
v5 = v1 * 2 # __mul__ 호출
print(v5) # Vector(6, 8)
비교 연산자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Money:
def __init__(self, amount):
self.amount = amount
def __eq__(self, other):
"""== 연산자"""
return self.amount == other.amount
def __lt__(self, other):
"""< 연산자"""
return self.amount < other.amount
def __le__(self, other):
"""<= 연산자"""
return self.amount <= other.amount
def __gt__(self, other):
"""> 연산자"""
return self.amount > other.amount
def __ge__(self, other):
""">= 연산자"""
return self.amount >= other.amount
def __str__(self):
return f"{self.amount:,}원"
m1 = Money(10000)
m2 = Money(5000)
print(m1 > m2) # True
print(m1 == m2) # False
print(m2 <= m1) # True
컨테이너 연산자
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Playlist:
def __init__(self, name):
self.name = name
self.songs = []
def __len__(self):
"""len() 함수"""
return len(self.songs)
def __getitem__(self, index):
"""[] 인덱싱"""
return self.songs[index]
def __setitem__(self, index, value):
"""[] 할당"""
self.songs[index] = value
def __contains__(self, item):
"""in 연산자"""
return item in self.songs
def add_song(self, song):
self.songs.append(song)
# 사용
playlist = Playlist("내가 좋아하는 노래")
playlist.add_song("Yesterday")
playlist.add_song("Imagine")
playlist.add_song("Bohemian Rhapsody")
print(len(playlist)) # 3
print(playlist[0]) # Yesterday
print("Imagine" in playlist) # True
for song in playlist: # __getitem__으로 반복 가능
print(f"♪ {song}")
🎯 학습 목표 4: 추상 베이스 클래스 활용하기 (ABC)
덕 타이핑은 유연하지만, 팀 프로젝트에서는 “반드시 구현해야 하는 메서드”를 명시하고 싶을 때가 있습니다.
추상 베이스 클래스(ABC)는 “이 메서드는 꼭 구현하세요!”라고 강제하는 방법입니다. 구현하지 않으면 인스턴스 생성 자체가 안 됩니다!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from abc import ABC, abstractmethod
class Shape(ABC):
"""도형 추상 클래스"""
@abstractmethod
def area(self):
"""넓이 계산 (구현 필수)"""
pass
@abstractmethod
def perimeter(self):
"""둘레 계산 (구현 필수)"""
pass
def describe(self):
"""일반 메서드 (선택)"""
return f"넓이: {self.area():.2f}, 둘레: {self.perimeter():.2f}"
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
def perimeter(self):
return 2 * 3.14159 * self.radius
# 사용
shapes = [
Rectangle(5, 3),
Circle(4)
]
for shape in shapes:
print(shape.describe())
# ❌ 추상 클래스는 인스턴스화 불가
# shape = Shape() # TypeError!
🎯 학습 목표 5: 실전 예제로 종합 정리하기
지금까지 배운 다형성, 덕 타이핑, ABC를 모두 활용한 결제 시스템을 만들어봅시다!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
from abc import ABC, abstractmethod
from datetime import datetime
class PaymentMethod(ABC):
"""결제 수단 추상 클래스"""
@abstractmethod
def pay(self, amount):
"""결제 처리 (추상 메서드)"""
pass
@abstractmethod
def refund(self, amount):
"""환불 처리 (추상 메서드)"""
pass
class CreditCard(PaymentMethod):
"""신용카드 결제"""
def __init__(self, card_number, owner):
self.card_number = card_number
self.owner = owner
def pay(self, amount):
print(f"💳 신용카드 결제: {amount:,}원")
print(f" 카드: {self.card_number[-4:].rjust(16, '*')}")
return True
def refund(self, amount):
print(f"💳 신용카드 환불: {amount:,}원")
return True
class BankTransfer(PaymentMethod):
"""계좌이체 결제"""
def __init__(self, account_number, bank):
self.account_number = account_number
self.bank = bank
def pay(self, amount):
print(f"🏦 계좌이체 결제: {amount:,}원")
print(f" 은행: {self.bank}")
return True
def refund(self, amount):
print(f"🏦 계좌이체 환불: {amount:,}원")
return True
class KakaoPay(PaymentMethod):
"""카카오페이 결제"""
def __init__(self, phone_number):
self.phone_number = phone_number
def pay(self, amount):
print(f"💛 카카오페이 결제: {amount:,}원")
print(f" 전화번호: {self.phone_number}")
return True
def refund(self, amount):
print(f"💛 카카오페이 환불: {amount:,}원")
return True
class PaymentProcessor:
"""결제 처리기 - 다형성 활용"""
def process_payment(self, payment_method: PaymentMethod, amount: int):
"""다형성 - 어떤 결제 수단이든 처리 가능"""
print(f"\n{'='*50}")
print(f"결제 처리 시작 ({datetime.now().strftime('%Y-%m-%d %H:%M:%S')})")
print(f"{'='*50}")
if payment_method.pay(amount):
print("✅ 결제 성공!")
return True
else:
print("❌ 결제 실패!")
return False
def process_refund(self, payment_method: PaymentMethod, amount: int):
"""환불 처리"""
print(f"\n{'='*50}")
print("환불 처리 시작")
print(f"{'='*50}")
if payment_method.refund(amount):
print("✅ 환불 성공!")
return True
else:
print("❌ 환불 실패!")
return False
# 사용
processor = PaymentProcessor()
# 다양한 결제 수단으로 결제
card = CreditCard("1234-5678-9012-3456", "홍길동")
processor.process_payment(card, 50000)
bank = BankTransfer("110-123-456789", "국민은행")
processor.process_payment(bank, 30000)
kakao = KakaoPay("010-1234-5678")
processor.process_payment(kakao, 20000)
# 환불
processor.process_refund(card, 50000)
실행 결과:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
==================================================
결제 처리 시작 (2025-04-07 10:00:00)
==================================================
💳 신용카드 결제: 50,000원
카드: ************3456
✅ 결제 성공!
==================================================
결제 처리 시작 (2025-04-07 10:00:01)
==================================================
🏦 계좌이체 결제: 30,000원
은행: 국민은행
✅ 결제 성공!
==================================================
결제 처리 시작 (2025-04-07 10:00:02)
==================================================
💛 카카오페이 결제: 20,000원
전화번호: 010-1234-5678
✅ 결제 성공!
==================================================
환불 처리 시작
==================================================
💳 신용카드 환불: 50,000원
✅ 환불 성공!
🧪 연습 문제
문제 1: 동물원 시스템
다형성을 활용한 동물원 시스템을 구현하세요:
요구사항:
Animal추상 클래스Lion,Elephant,Penguin구현- 각 동물은
speak(),move()메서드 구현
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from abc import ABC, abstractmethod
class Animal(ABC):
def __init__(self, name, age):
self.name = name
self.age = age
@abstractmethod
def speak(self):
pass
@abstractmethod
def move(self):
pass
def introduce(self):
return f"{self.name} ({self.age}살)"
class Lion(Animal):
def speak(self):
return f"{self.name}: 어흥!!"
def move(self):
return f"{self.name}이(가) 달립니다 🦁"
class Elephant(Animal):
def speak(self):
return f"{self.name}: 뿌우우~"
def move(self):
return f"{self.name}이(가) 천천히 걷습니다 🐘"
class Penguin(Animal):
def speak(self):
return f"{self.name}: 꽥꽥!"
def move(self):
return f"{self.name}이(가) 뒤뚱뒤뚱 걷습니다 🐧"
# 동물원
zoo = [
Lion("심바", 5),
Elephant("점보", 10),
Penguin("뽀로로", 2)
]
for animal in zoo:
print(animal.introduce())
print(animal.speak())
print(animal.move())
print()
💡 실전 팁 & 주의사항
Tip 1: 덕 타이핑 활용하기
Python에서는 타입보다 동작이 중요합니다. isinstance() 체크를 최소화하세요.
1
2
3
4
5
6
7
8
# ❌ 타입 체크
def process(obj):
if isinstance(obj, MyClass):
obj.method()
# ✅ 덕 타이핑
def process(obj):
obj.method() # method()만 있으면 OK!
Tip 2: ABC로 명확한 인터페이스 정의
추상 베이스 클래스를 사용하면 구현해야 할 메서드를 명확히 할 수 있습니다.
1
2
3
4
5
6
from abc import ABC, abstractmethod
class PaymentMethod(ABC):
@abstractmethod
def pay(self, amount):
pass
Tip 3: 연산자 오버로딩은 직관적으로
연산자를 재정의할 때는 사용자가 예상할 수 있는 동작으로 구현하세요.
1
2
3
4
5
6
7
# ✅ 직관적
def __add__(self, other):
return Money(self.amount + other.amount)
# ❌ 혼란스러움
def __add__(self, other):
return Money(self.amount * other.amount) # +인데 곱셈?
📝 오늘 배운 내용 정리
| 개념 | 설명 | 예시 |
|---|---|---|
| 다형성 | 같은 인터페이스, 다른 구현 | shape.area() |
| 덕 타이핑 | 타입보다 동작 중시 | obj.method() |
| 연산자 오버로딩 | 연산자 재정의 | __add__, __eq__ |
| ABC | 추상 베이스 클래스 | @abstractmethod |
주요 특수 메서드
산술 연산:
__add__(+),__sub__(-),__mul__(*),__truediv__(/)
비교 연산:
__eq__(==),__lt__(<),__gt__(>),__le__(<=),__ge__(>=)
컨테이너:
__len__(len),__getitem__([]),__contains__(in)
문자열:
__str__(str),__repr__(repr)
🔗 관련 자료
📚 이전 학습
Day 37: 캡슐화와 정보 은닉 심화 ⭐⭐⭐⭐
어제는 캡슐화 원칙 심화, Private 변수와 Property 데코레이터 고급 활용, 불변 객체(Immutable Object) 설계를 배웠습니다!
📚 다음 학습
Day 39: 특수 메서드 (str, repr 등) ⭐⭐⭐⭐
내일은 __str__과 __repr__ 차이, __call__ 호출 가능 객체, __enter__와 __exit__ (Context Manager), 완전한 특수 메서드 가이드를 배웁니다!
“늦었다고 생각할 때가 가장 빠른 시기입니다!” 🚀
Day 38/100 Phase 4: 객체지향 프로그래밍 #100DaysOfPython
