Đa hình (Polymorphism)
Polymorphism là gì?
Polymorphism (Đa hình) có nghĩa là "nhiều hình dạng". Trong OOP, polymorphism cho phép:
- Cùng một hành động nhưng thể hiện khác nhau ở các đối tượng khác nhau
- Sử dụng một interface chung cho các kiểu dữ liệu khác nhau
- Xử lý các objects khác nhau thông qua cùng một interface
Tại sao cần Polymorphism?
1. Linh hoạt
Code có thể làm việc với nhiều kiểu đối tượng khác nhau.
2. Dễ mở rộng
Thêm class mới mà không cần thay đổi code hiện tại.
3. Code ngắn gọn
Giảm code lặp lại, viết logic chung cho nhiều kiểu dữ liệu.
4. Dễ bảo trì
Thay đổi hành vi của một class không ảnh hưởng đến code khác.
Ví dụ minh họa trong thế giới thực
Hãy tưởng tượng bạn bảo các con vật "phát ra tiếng kêu":
- Chó → sủa "Gâu gâu"
- Mèo → kêu "Meo meo"
- Chim → hót "Chip chip"
Cùng một hành động ("phát ra tiếng kêu") nhưng mỗi con vật thể hiện khác nhau → Đó là polymorphism!
Các loại Polymorphism trong Python
1. Duck Typing
"If it walks like a duck and quacks like a duck, it must be a duck"
Python không quan tâm object thuộc class nào, chỉ quan tâm object có phương thức cần thiết không.
class Dog:
def speak(self):
return "Gâu gâu!"
class Cat:
def speak(self):
return "Meo meo!"
class Cow:
def speak(self):
return "Ummm!"
def animal_sound(animal):
"""Hàm này không quan tâm animal là class gì,
chỉ cần có method speak()"""
print(animal.speak())
# Sử dụng
dog = Dog()
cat = Cat()
cow = Cow()
animal_sound(dog) # Gâu gâu!
animal_sound(cat) # Meo meo!
animal_sound(cow) # Ummm!2. Method Overriding (Ghi đè phương thức)
Class con định nghĩa lại phương thức của class cha.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} phát ra tiếng kêu"
class Dog(Animal):
def speak(self): # Override
return f"{self.name} sủa: Gâu gâu!"
class Cat(Animal):
def speak(self): # Override
return f"{self.name} kêu: Meo meo!"
class Bird(Animal):
def speak(self): # Override
return f"{self.name} hót: Chip chip!"
# Sử dụng polymorphism
animals = [
Dog("Buddy"),
Cat("Kitty"),
Bird("Tweety"),
Dog("Max")
]
for animal in animals:
print(animal.speak())
# Buddy sủa: Gâu gâu!
# Kitty kêu: Meo meo!
# Tweety hót: Chip chip!
# Max sủa: Gâu gâu!3. Operator Overloading (Nạp chồng toán tử)
Định nghĩa lại cách hoạt động của toán tử (+, -, *, ...) cho class của bạn.
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
"""Nạp chồng toán tử +"""
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other):
"""Nạp chồng toán tử -"""
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar):
"""Nạp chồng toán tử *"""
return Vector(self.x * scalar, self.y * scalar)
def __str__(self):
return f"Vector({self.x}, {self.y})"
# Sử dụng
v1 = Vector(2, 3)
v2 = Vector(1, 4)
v3 = v1 + v2 # Gọi __add__
print(v3) # Vector(3, 7)
v4 = v1 - v2 # Gọi __sub__
print(v4) # Vector(1, -1)
v5 = v1 * 3 # Gọi __mul__
print(v5) # Vector(6, 9)Polymorphism với Inheritance
class Shape:
"""Base class"""
def __init__(self, color):
self.color = color
def area(self):
"""Sẽ được override ở class con"""
pass
def describe(self):
return f"Đây là hình {self.color}"
class Rectangle(Shape):
def __init__(self, color, width, height):
super().__init__(color)
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Circle(Shape):
def __init__(self, color, radius):
super().__init__(color)
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
class Triangle(Shape):
def __init__(self, color, base, height):
super().__init__(color)
self.base = base
self.height = height
def area(self):
return 0.5 * self.base * self.height
# Polymorphic function
def print_shape_info(shape):
"""Hàm này nhận bất kỳ Shape nào"""
print(f"{shape.describe()}")
print(f"Diện tích: {shape.area():.2f}")
print()
# Sử dụng
shapes = [
Rectangle("xanh", 5, 3),
Circle("đỏ", 4),
Triangle("vàng", 6, 4)
]
for shape in shapes:
print_shape_info(shape)
# Đây là hình xanh
# Diện tích: 15.00
#
# Đây là hình đỏ
# Diện tích: 50.27
#
# Đây là hình vàng
# Diện tích: 12.00Polymorphism với Abstract Base Class
from abc import ABC, abstractmethod
class Payment(ABC):
"""Abstract base class cho các phương thức thanh toán"""
@abstractmethod
def process_payment(self, amount):
"""Phương thức trừu tượng - bắt buộc implement"""
pass
@abstractmethod
def get_payment_info(self):
pass
class CreditCardPayment(Payment):
def __init__(self, card_number, cvv):
self.card_number = card_number
self.cvv = cvv
def process_payment(self, amount):
print(f"Đang xử lý thanh toán {amount:,}đ qua thẻ tín dụng...")
print(f"Số thẻ: ****{self.card_number[-4:]}")
return True
def get_payment_info(self):
return f"Thẻ tín dụng ****{self.card_number[-4:]}"
class MomoPayment(Payment):
def __init__(self, phone_number):
self.phone_number = phone_number
def process_payment(self, amount):
print(f"Đang xử lý thanh toán {amount:,}đ qua Momo...")
print(f"Số điện thoại: {self.phone_number}")
return True
def get_payment_info(self):
return f"Momo - {self.phone_number}"
class BankTransferPayment(Payment):
def __init__(self, account_number, bank_name):
self.account_number = account_number
self.bank_name = bank_name
def process_payment(self, amount):
print(f"Đang xử lý thanh toán {amount:,}đ qua chuyển khoản...")
print(f"Ngân hàng: {self.bank_name}")
print(f"Số TK: {self.account_number}")
return True
def get_payment_info(self):
return f"{self.bank_name} - {self.account_number}"
# Polymorphic function
def checkout(payment_method, amount):
"""Hàm thanh toán - nhận bất kỳ Payment nào"""
print(f"=== THANH TOÁN ===")
print(f"Phương thức: {payment_method.get_payment_info()}")
payment_method.process_payment(amount)
print("Thanh toán thành công!\n")
# Sử dụng
payments = [
CreditCardPayment("1234567890123456", "123"),
MomoPayment("0123456789"),
BankTransferPayment("0011223344", "Vietcombank")
]
for payment in payments:
checkout(payment, 500000)Ví dụ thực tế: Hệ thống File
class File:
"""Base class cho tất cả file types"""
def __init__(self, name, size):
self.name = name
self.size = size
def open(self):
print(f"Đang mở file {self.name}")
def display_info(self):
print(f"File: {self.name}")
print(f"Size: {self.size} bytes")
class TextFile(File):
def __init__(self, name, size, encoding="utf-8"):
super().__init__(name, size)
self.encoding = encoding
def open(self):
print(f"Đang mở text file {self.name} với encoding {self.encoding}")
def read_content(self):
print(f"Đọc nội dung text file...")
class ImageFile(File):
def __init__(self, name, size, resolution):
super().__init__(name, size)
self.resolution = resolution
def open(self):
print(f"Đang mở image file {self.name} ({self.resolution})")
def display_preview(self):
print(f"Hiển thị preview hình ảnh...")
class VideoFile(File):
def __init__(self, name, size, duration):
super().__init__(name, size)
self.duration = duration
def open(self):
print(f"Đang mở video file {self.name} (thời lượng: {self.duration}s)")
def play(self):
print(f"Đang phát video...")
# File manager sử dụng polymorphism
class FileManager:
def __init__(self):
self.files = []
def add_file(self, file):
self.files.append(file)
print(f"Đã thêm {file.name}")
def open_all_files(self):
print("\n=== MỞ TẤT CẢ FILE ===")
for file in self.files:
file.open() # Polymorphic call
print()
def show_file_info(self):
print("\n=== THÔNG TIN FILE ===")
for file in self.files:
file.display_info()
print()
# Sử dụng
manager = FileManager()
manager.add_file(TextFile("readme.txt", 1024, "utf-8"))
manager.add_file(ImageFile("photo.jpg", 2048000, "1920x1080"))
manager.add_file(VideoFile("movie.mp4", 50000000, 120))
manager.open_all_files()
manager.show_file_info()Built-in Polymorphism trong Python
Python có nhiều ví dụ về polymorphism tích hợp sẵn.
1. len() function
# len() hoạt động với nhiều kiểu dữ liệu khác nhau
print(len("Hello")) # 5 (string)
print(len([1, 2, 3])) # 3 (list)
print(len({'a': 1, 'b': 2})) # 2 (dict)
print(len({1, 2, 3})) # 3 (set)
# Tự định nghĩa len() cho class của bạn
class MyCollection:
def __init__(self, items):
self.items = items
def __len__(self):
return len(self.items)
collection = MyCollection([1, 2, 3, 4, 5])
print(len(collection)) # 52. Toán tử +
# + hoạt động khác nhau với các kiểu dữ liệu
print(5 + 3) # 8 (số)
print("Hello" + " World") # Hello World (string)
print([1, 2] + [3, 4]) # [1, 2, 3, 4] (list)3. Iteration
# Iteration hoạt động với nhiều kiểu dữ liệu
for item in [1, 2, 3]: # list
print(item)
for char in "Hello": # string
print(char)
for key in {'a': 1, 'b': 2}: # dict
print(key)
# Tự định nghĩa iteration cho class
class Counter:
def __init__(self, max):
self.max = max
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n < self.max:
self.n += 1
return self.n
raise StopIteration
counter = Counter(5)
for num in counter:
print(num) # 1, 2, 3, 4, 5Ví dụ nâng cao: Game với Polymorphism
from abc import ABC, abstractmethod
class Character(ABC):
"""Abstract base class cho nhân vật game"""
def __init__(self, name, health, attack_power):
self.name = name
self.health = health
self.attack_power = attack_power
@abstractmethod
def attack(self, target):
"""Phương thức tấn công - mỗi class implement khác nhau"""
pass
@abstractmethod
def special_ability(self):
"""Kỹ năng đặc biệt"""
pass
def take_damage(self, damage):
self.health -= damage
print(f"{self.name} nhận {damage} sát thương. HP còn: {self.health}")
def is_alive(self):
return self.health > 0
class Warrior(Character):
def __init__(self, name):
super().__init__(name, health=150, attack_power=25)
self.armor = 20
def attack(self, target):
print(f"{self.name} đánh chém {target.name}!")
target.take_damage(self.attack_power)
def special_ability(self):
print(f"{self.name} sử dụng Rage! Tăng sức mạnh!")
self.attack_power += 10
class Mage(Character):
def __init__(self, name):
super().__init__(name, health=80, attack_power=35)
self.mana = 100
def attack(self, target):
if self.mana >= 10:
print(f"{self.name} phóng phép thuật vào {target.name}!")
target.take_damage(self.attack_power)
self.mana -= 10
else:
print(f"{self.name} hết mana!")
def special_ability(self):
print(f"{self.name} hồi phục mana!")
self.mana = 100
class Archer(Character):
def __init__(self, name):
super().__init__(name, health=100, attack_power=30)
self.arrows = 20
def attack(self, target):
if self.arrows > 0:
print(f"{self.name} bắn tên vào {target.name}!")
target.take_damage(self.attack_power)
self.arrows -= 1
else:
print(f"{self.name} hết tên!")
def special_ability(self):
print(f"{self.name} bắn mũi tên lửa!")
self.attack_power += 15
# Game manager
class GameArena:
def battle(self, char1, char2):
print(f"\n=== {char1.name} vs {char2.name} ===\n")
round_num = 1
while char1.is_alive() and char2.is_alive():
print(f"--- Round {round_num} ---")
# Char1 attack
char1.attack(char2)
if not char2.is_alive():
print(f"\n{char1.name} thắng!")
break
# Char2 attack
char2.attack(char1)
if not char1.is_alive():
print(f"\n{char2.name} thắng!")
break
round_num += 1
print()
# Chơi game
warrior = Warrior("Conan")
mage = Mage("Gandalf")
archer = Archer("Legolas")
arena = GameArena()
arena.battle(warrior, mage)Method Overloading (Nạp chồng phương thức)
Python không hỗ trợ method overloading truyền thống, nhưng có thể mô phỏng bằng default parameters hoặc *args.
class Calculator:
def add(self, *args):
"""Có thể cộng 2 số, 3 số, hoặc nhiều số"""
return sum(args)
def multiply(self, a, b=1, c=1):
"""Có thể nhân 1, 2, hoặc 3 số"""
return a * b * c
calc = Calculator()
print(calc.add(5, 3)) # 8
print(calc.add(5, 3, 2)) # 10
print(calc.add(1, 2, 3, 4, 5)) # 15
print(calc.multiply(5)) # 5
print(calc.multiply(5, 3)) # 15
print(calc.multiply(5, 3, 2)) # 30Tổng kết
- Polymorphism cho phép cùng interface nhưng hành vi khác nhau
- Duck Typing: Python không quan tâm kiểu, chỉ quan tâm có method không
- Method Overriding: Class con định nghĩa lại method của class cha
- Operator Overloading: Định nghĩa lại toán tử cho class tùy chỉnh
- Polymorphism làm code linh hoạt, dễ mở rộng và bảo trì
Trong bài tiếp theo, chúng ta sẽ tìm hiểu về Special Methods (Magic Methods)!