Kế thừa Python: đơn, ghi đè, đa kế thừa & best practices

1. Tổng quan về kế thừa trong Python

Trong Python, kế thừa là cơ chế cho phép lớp con thừa hưởng các chức năng và thuộc tính từ lớp cha. Nhờ đó, khả năng tái sử dụng mã được cải thiện và việc bảo trì trở nên hiệu quả hơn. Đây là một trong những khái niệm quan trọng của lập trình hướng đối tượng (OOP), đặc biệt hữu ích trong phát triển hệ thống quy mô lớn và các dự án dài hạn.

Vai trò cơ bản của kế thừa

  • Khả năng tái sử dụng mã: Vì có thể dùng lại chức năng của lớp đã viết cho các lớp khác, nên tránh được mã trùng lặp.
  • Dễ bảo trì: Do thay đổi ở lớp cha tự động phản ánh xuống lớp con, việc sửa lỗi và mở rộng chức năng được thực hiện hiệu quả.
class ParentClass:
    def greet(self):
        print("Xin chào, tôi là lớp cha.")

class ChildClass(ParentClass):
    def greet(self):
        print("Xin chào, tôi là lớp con.")
Trong ví dụ này, ChildClass ghi đè phương thức của ParentClass。Vì phương thức greet đã được ghi đè, nên lớp con sẽ hiển thị lời chào riêng của nó。

2. Kế thừa đơn trong Python

Kế thừa đơn là hình thức trong đó một lớp con kế thừa các chức năng từ một lớp cha duy nhất. Đây là hình thức kế thừa cơ bản trong Python, vừa giữ cho mã đơn giản vừa đảm bảo khả năng mở rộng。

Cú pháp cơ bản và ví dụ về kế thừa đơn

class Car:
    def __init__(self, brand, color):
        self.brand = brand
        self.color = color

    def describe(self):
        print(f"Chiếc xe này là {self.brand} màu {self.color}。")

class ElectricCar(Car):
    def __init__(self, brand, color, battery_size):
        super().__init__(brand, color)
        self.battery_size = battery_size

    def describe_battery(self):
        print(f"Dung lượng pin là {self.battery_size} kWh。")
Trong ví dụ này, lớp ElectricCar kế thừa các chức năng của lớp Car đồng thời bổ sung chức năng mô tả dung lượng pin. Sử dụng super() để gọi constructor của lớp cha và khởi tạo các thuộc tính chung (thương hiệu và màu sắc)。
侍エンジニア塾

3. Ghi đè phương thức

Ghi đè là tính năng cho phép lớp con định nghĩa lại các phương thức của lớp cha. Nhờ đó, bạn có thể tận dụng các phương thức của lớp cha đồng thời thay đổi hành vi ở lớp con.

Ví dụ về ghi đè

class Animal:
    def speak(self):
        print("Tiếng kêu của động vật")

class Dog(Animal):
    def speak(self):
        print("Gâu gâu!")
Trong ví dụ này, lớp Dog ghi đè phương thức speak của lớp Animal. Nhờ vậy, với một thể hiện của lớp Dog, sẽ in ra 「Gâu gâu」, còn với thể hiện của lớp Animal sẽ hiển thị 「Tiếng kêu của động vật」.

4. Đa kế thừa

Đa kế thừa cho phép một lớp con kế thừa từ nhiều lớp cha. Nhờ đó, bạn có thể hợp nhất các chức năng của các lớp khác nhau vào một lớp, nhưng cũng cần lưu ý vì việc triển khai có thể trở nên phức tạp.

Ví dụ và lưu ý về đa kế thừa

class A:
    def greet(self):
        print("Lời chào của A")

class B:
    def greet(self):
        print("Lời chào của B")

class C(A, B):
    pass

c = C()
c.greet()  # Sẽ hiển thị "Lời chào của A" (theo MRO, lớp đầu tiên được ưu tiên)
Trong Python, MRO (Method Resolution Order) quyết định phương thức của lớp cha nào sẽ được gọi. Để kiểm tra thứ tự này, hãy dùng C.mro(). Đa kế thừa rất mạnh mẽ, nhưng cần chú ý đến xung đột giữa các lớp cha và thứ tự của phương thức khi sử dụng.

5. Ví dụ thực tiễn về việc sử dụng kế thừa

Kế thừa hữu ích trong rất nhiều tình huống của lập trình hằng ngày. Chẳng hạn, trong hệ thống quản lý nhân viên của doanh nghiệp, bằng cách kế thừa từ một lớp nhân viên cơ bản để tạo ra các lớp có chức vụ cụ thể, ta có thể tái sử dụng và mở rộng mã.

Ví dụ thực tiễn về hệ thống quản lý nhân viên

class Employee:
    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.pay = pay

    def fullname(self):
        return f'{self.first} {self.last}'

class Manager(Employee):
    def __init__(self, first, last, pay, employees=None):
        super().__init__(first, last, pay)
        self.employees = employees if employees is not None else []

    def add_employee(self, employee):
        if employee not in self.employees:
            self.employees.append(employee)

    def print_employees(self):
        for emp in self.employees:
            print(emp.fullname())
Trong ví dụ này, lớp Manager kế thừa từ lớp Employee và bổ sung chức năng quản lý nhân viên. Vừa duy trì các chức năng chung của lớp cha, vừa mở rộng các chức năng theo từng chức vụ cụ thể.

6. Thực tiễn tốt nhất về kế thừa và so sánh với composition

Kế thừa rất mạnh mẽ, nhưng nếu lạm dụng sẽ có nguy cơ làm mã trở nên phức tạp. Đặc biệt, đa kế thừa có thể làm mối quan hệ giữa các lớp trở nên phức tạp, vì vậy nên sử dụng một cách thận trọng. Trong những trường hợp như vậy, nên ưu tiên sử dụng composition thay vì kế thừa.

Ví dụ về composition

Composition là một mẫu thiết kế trong đó một lớp sở hữu một lớp khác như một thành phần (instance) để ủy quyền chức năng.
class Engine:
    def start(self):
        print("Động cơ đã khởi động.")

class Car:
    def __init__(self, engine):
        self.engine = engine

    def start(self):
        self.engine.start()

engine = Engine()
car = Car(engine)
car.start()  # "Động cơ đã khởi động." sẽ được hiển thị
Như vậy, composition là cách chia sẻ chức năng giữa các lớp mà không cần dùng kế thừa. Bằng cách chỉ đưa vào những chức năng cần thiết, mã sẽ linh hoạt hơn và dễ quản lý hơn.

7. Tổng kết

Kế thừa trong Python là một công cụ mạnh mẽ để nâng cao khả năng tái sử dụng mã và khả năng mở rộng. Bằng cách hiểu các kỹ thuật như kế thừa đơn, đa kế thừa và ghi đè, bạn có thể tạo ra các chương trình hiệu quả và dễ bảo trì. Mặt khác, cần cân nhắc việc lựa chọn giữa kế thừa và composition, và thiết kế cho phù hợp. Thành thạo việc áp dụng kế thừa đúng cách sẽ giúp bạn xây dựng một codebase linh hoạt và vững chắc.