## Tính Trừu Tượng trong Python
### Khái niệm về Tính Trừu Tượng
Tính Trừu Tượng (Abstraction) là một khái niệm quan trọng trong lập trình hướng đối tượng. Nó cho phép chúng ta ẩn đi các chi tiết cụ thể của một đối tượng và chỉ tập trung vào các thông tin quan trọng hoặc các phương thức cần thiết. Tính Trừu Tượng giúp chúng ta tạo ra các lớp và đối tượng mà có thể được sử dụng một cách linh hoạt và tái sử dụng trong các phần khác của ứng dụng.
### Mô tả về Abstract Class, Abstract Method và Interface trong Python
Trong Python, chúng ta có các khái niệm sau liên quan đến Tính Trừu Tượng:
1. **Abstract Class (Lớp Trừu Tượng)**: Là một lớp mà không thể tạo ra đối tượng cụ thể từ lớp này. Abstract class thường chứa ít nhất một phương thức trừu tượng và dùng để định nghĩa giao diện cho các lớp con.
2. **Abstract Method (Phương Thức Trừu Tượng)**: Là một phương thức trong lớp trừu tượng mà không có triển khai (implementation) cụ thể trong lớp đó. Các lớp con buộc phải triển khai các phương thức trừu tượng này.
3. **Interface (Giao Diện)**: Trong Python, không có kiểu giao diện (interface) giống như trong một số ngôn ngữ khác. Tuy nhiên, các abstract class và abstract method thường được sử dụng để định nghĩa giao diện trong Python.
### Từ khoá pass khi khai báo abstract method
Khi bạn định nghĩa một phương thức trừu tượng trong một lớp trừu tượng, bạn sử dụng từ khoá `pass` để chỉ ra rằng phương thức này chưa có triển khai cụ thể. Ví dụ:
```python
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
```
### Sử dụng lớp trừu tượng và phương thức trừu tượng
Khi bạn muốn tạo một lớp con từ một lớp trừu tượng, bạn buộc phải triển khai (override) tất cả các phương thức trừu tượng đã được định nghĩa trong lớp cha. Dưới đây là một ví dụ về cách sử dụng lớp trừu tượng:
```python
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)
```
Trong ví dụ trên, lớp `Rectangle` kế thừa từ lớp trừu tượng `Shape` và triển khai lại các phương thức `area` và `perimeter`.
<hr>
# Tính Kế Thừa Đa Cấp trong Python
### Khái niệm về Kế Thừa Đa Cấp
Tính Kế Thừa Đa Cấp (Multilevel Inheritance) là một trong các tính chất của lập trình hướng đối tượng, cho phép bạn tạo ra các lớp con từ các lớp con của một lớp khác. Nghĩa là lớp con của lớp cha cũng có thể là lớp cha của một lớp khác.
Kế thừa đa cấp cho phép chúng ta tái sử dụng mã nguồn, xây dựng các mức độ phức tạp và cung cấp tính linh hoạt trong thiết kế của chương trình.
### Ví dụ và trường hợp sử dụng Tính Kế Thừa Đa Cấp
Dưới đây là một ví dụ minh họa về tính kế thừa đa cấp trong Python:
```python
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
class Kitten(Cat):
def speak(self):
return f"{self.name} is a kitten and says Meow Meow!"
```
Trong ví dụ trên, chúng ta có một lớp cơ sở là `Animal`, và từ đó chúng ta tạo ra hai lớp con `Dog` và `Cat`. Sau đó, chúng ta tạo một lớp con khác `Kitten` từ lớp `Cat`. Lớp `Kitten` kế thừa từ `Cat`, và `Cat` kế thừa từ `Animal`. Khi chúng ta gọi phương thức `speak()` của một đối tượng `Kitten`, nó sẽ sử dụng phương thức `speak()` của lớp con gần nhất, nếu không có, nó sẽ tìm kiếm trong các lớp cha cho đến khi tìm thấy.
Ví dụ này thể hiện cách sử dụng tính kế thừa đa cấp để xây dựng một cây phân loại động vật, và mỗi động vật có khả năng "nói" theo cách riêng của chúng.
<hr>
# Tính đa Kế Thừa trong Python
Trong Python, tính đa kế thừa (multiple inheritance) cho phép một lớp con kế thừa từ nhiều lớp cha. Điều này có nghĩa rằng lớp con có thể sử dụng các thuộc tính và phương thức từ nhiều lớp cha khác nhau. Để sử dụng tính đa kế thừa trong Python, bạn chỉ cần liệt kê tất cả các lớp cha bạn muốn kế thừa, ngăn cách bằng dấu phẩy, khi định nghĩa lớp con.
Dưới đây là một ví dụ minh họa về tính đa kế thừa trong Python:
```python
class Parent1:
def method1(self):
print("Phương thức từ Parent1")
class Parent2:
def method2(self):
print("Phương thức từ Parent2")
class Child(Parent1, Parent2):
def method3(self):
print("Phương thức từ Child")
# Tạo một đối tượng của lớp Child
child = Child()
# Sử dụng các phương thức từ cả hai lớp cha
child.method1()
child.method2()
child.method3()
```
Kết quả của đoạn mã trên sẽ là:
```
Phương thức từ Parent1
Phương thức từ Parent2
Phương thức từ Child
```
Như bạn thấy, lớp `Child` kế thừa từ cả hai lớp `Parent1` và `Parent2`, và nó có thể sử dụng các phương thức từ cả hai lớp cha.
Tuy nhiên, khi sử dụng tính đa kế thừa, bạn cần cẩn trọng để tránh xung đột tên phương thức hoặc thuộc tính giữa các lớp cha. Trong trường hợp có xung đột, Python sẽ ưu tiên lấy phương thức hoặc thuộc tính từ lớp cha đầu tiên trong danh sách kế thừa.
<hr>
## Tính Đa Hình Động và Tĩnh
### Tính Đa Hình Động (Dynamic Polymorphism)
Tính Đa Hình Động (Dynamic Polymorphism) cho phép chúng ta gọi cùng một phương thức trên các đối tượng thuộc các lớp khác nhau và phương thức sẽ được thực thi dựa trên loại đối tượng đó. Tính đa hình động thường được thực hiện thông qua ghi đè phương thức (method overriding).
### Tính Đa Hình Tĩnh (Static Polymorphism)
Tính Đa Hình Tĩnh (Static Polymorphism) cho phép chúng ta cung cấp nhiều triển khai (implementations) cho cùng một phương thức trong một lớp dựa trên số lượng và loại đối số (parameters) hoặc kiểu dữ liệu của đối số. Tính đa hình tĩnh thường được thực hiện thông qua quá tải phương thức (method overloading).
### So sánh Tính Đa Hình Động và Tính Đa Hình Tĩnh
- **Tính Đa Hình Động**:
- Phương thức thực thi dựa trên loại đối tượng thực tế tại thời điểm runtime.
- Thường được thực hiện thông qua ghi đè phương thức.
- Được sử dụng khi chúng ta muốn cung cấp cùng một giao diện chung cho các lớp con, nhưng mỗi lớp con có cách thực hiện khác nhau.
- **Tính Đa Hình Tĩnh**:
- Phương thức thực thi dựa trên kiểu và số lượng đối số đã truyền vào tại thời điểm compile time.
- Thường được thực hiện thông qua quá tải phương thức (method overloading).
- Được sử dụng khi chúng ta muốn cung cấp nhiều phiên bản của cùng một phương thức với các loại đối số khác nhau.
### Ví dụ về cả hai loại Tính Đa Hình trong Python
#### Tính Đa Hình Động (Dynamic Polymorphism):
```python
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
def animal_sound(animal):
return animal.speak()
dog = Dog()
cat = Cat()
print(animal_sound(dog)) # Kết quả: "Woof!"
print(animal_sound(cat)) # Kết quả: "Meow!"
```
#### Tính Đa Hình Tĩnh (Static Polymorphism):
```python
class Calculator:
def add(self, a, b):
return a + b
def add(self, a, b, c):
return a + b + c
calc = Calculator()
print(calc.add(1, 2)) # Kết quả: TypeError (Không tìm thấy phương thức add với 2 đối số)
print(calc.add(1, 2, 3)) # Kết quả: 6 (Sử dụng phương thức add với 3 đối số)
```
Trong ví dụ trên, chúng ta thấy rằng phương thức `add` của lớp `Calculator` đã được quá tải với hai phiên bản, một phiên bản với 2 đối số và một phiên bản với 3 đối số. Chúng ta có thể gọi phương thức `add` với số lượng đối số phù hợp.