Python Basics

Lập trình hướng đối tượng trong Python

Lập trình hướng đối tượng tổ chức chương trình thành các đối tượng sở hữu trạng thái và cung cấp hành vi qua giao diện rõ ràng.

Lập trình hướng đối tượng trong Python là gì?

Lập trình hướng đối tượng (OOP) mô hình hóa hệ thống bằng các đối tượng cộng tác với nhau. Mỗi đối tượng kết hợp trạng thái với các thao tác hợp lệ trên trạng thái đó, giúp chương trình lớn phân chia trách nhiệm rõ ràng hơn.

Bốn nguyên lý cốt lõi của OOP

  • Đóng gói: đặt trạng thái và hành vi liên quan cùng nhau, chỉ cho truy cập qua phương thức hoặc property được kiểm soát.
  • Trừu tượng: cung cấp giao diện cần thiết và che giấu chi tiết triển khai mà bên gọi không cần biết.
  • Kế thừa: tạo lớp chuyên biệt từ lớp có sẵn khi giữa chúng thực sự có quan hệ is-a.
  • Đa hình: gọi cùng một thao tác trên nhiều loại đối tượng và mỗi loại thực hiện hành vi phù hợp.

Thiết kế của ví dụ

  1. BankAccount sở hữu trạng thái số dư và kiểm tra khoản tiền nạp.
  2. SavingsAccount tái sử dụng hợp đồng của lớp tài khoản cơ sở qua kế thừa.
  3. SavingsAccount ghi đè month_end để cộng lãi.
  4. Vòng lặp xử lý mọi tài khoản qua cùng một giao diện.
Gợi ý thiết kế: Ưu tiên composition khi một đối tượng chỉ sử dụng đối tượng khác. Chỉ dùng kế thừa khi có thể thay lớp cha bằng lớp con mà chương trình vẫn đúng.

Ví dụ cơ bản

Chạy code →
main.py
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = float(balance)

    @property
    def balance(self):
        return self._balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("Deposit must be positive")
        self._balance += amount

    def month_end(self):
        return "No interest"


class SavingsAccount(BankAccount):
    def __init__(self, owner, balance, interest_rate):
        super().__init__(owner, balance)
        self.interest_rate = interest_rate

    def month_end(self):
        self._balance *= 1 + self.interest_rate
        return "Interest applied"


accounts = [
    BankAccount("Ada", 100),
    SavingsAccount("Lin", 200, 0.05),
]
accounts[0].deposit(50)

for account in accounts:
    status = account.month_end()
    print(f"{account.owner}: {account.balance:.2f} ({status})")

Kết quả dự kiến

Ada: 150.00 (No interest)
Lin: 210.00 (Interest applied)

Thực hành bốn nguyên lý OOP

Các ví dụ tập trung sau minh họa cách Python thể hiện đóng gói, trừu tượng, kế thừa và đa hình trong code thực tế.

Đóng gói

Chạy code →

đặt trạng thái và hành vi liên quan cùng nhau, chỉ cho truy cập qua phương thức hoặc property được kiểm soát.

encapsulation.py
class BankAccount:
    def __init__(self, balance=0):
        # Double underscores prevent accidental direct access.
        self.__balance = balance

    @property
    def balance(self):
        # Expose read access without exposing mutation.
        return self.__balance

    def deposit(self, amount):
        # Keep validation next to the state it protects.
        if amount <= 0:
            raise ValueError("Amount must be positive")
        self.__balance += amount


account = BankAccount(100)
account.deposit(50)
print(account.balance)  # 150

Trừu tượng

Chạy code →

cung cấp giao diện cần thiết và che giấu chi tiết triển khai mà bên gọi không cần biết.

abstraction.py
from abc import ABC, abstractmethod


class PaymentMethod(ABC):
    @abstractmethod
    def pay(self, amount):
        # Callers know what pay does, not how each provider does it.
        pass


class CreditCard(PaymentMethod):
    def pay(self, amount):
        # The concrete class hides its payment implementation.
        return f"Paid ${amount:.2f} by card"


payment = CreditCard()
print(payment.pay(25))

Kế thừa

Chạy code →

tạo lớp chuyên biệt từ lớp có sẵn khi giữa chúng thực sự có quan hệ is-a.

inheritance.py
class Employee:
    def __init__(self, name):
        self.name = name

    def describe(self):
        return self.name


class Developer(Employee):
    def __init__(self, name, language):
        # Reuse initialization from the parent class.
        super().__init__(name)
        self.language = language

    def describe(self):
        # Extend inherited behavior instead of duplicating it.
        return f"{super().describe()} writes {self.language}"


print(Developer("Ada", "Python").describe())

Đa hình

Chạy code →

gọi cùng một thao tác trên nhiều loại đối tượng và mỗi loại thực hiện hành vi phù hợp.

polymorphism.py
class EmailNotification:
    def send(self, message):
        return f"Email: {message}"


class SmsNotification:
    def send(self, message):
        return f"SMS: {message}"


def notify(channels, message):
    for channel in channels:
        # The same call selects behavior from each concrete object.
        print(channel.send(message))


channels = [EmailNotification(), SmsNotification()]
notify(channels, "Order shipped")

Cách hoạt động

BankAccount đóng gói số dư và chỉ cho phép thay đổi qua các thao tác được kiểm soát. SavingsAccount kế thừa giao diện đó và ghi đè month_end. Vòng lặp sử dụng hai loại đối tượng theo cùng một cách nên Python chọn phương thức dựa trên kiểu thực tế khi chạy.

Thay đổi giá trị và chạy chương trình bằng trình chạy Python online của CodeUtility mà không cần cài Python.

Bài tập mở rộng

Hãy sửa code theo các bài tập dưới đây để hiểu rõ cách hoạt động thay vì chỉ sao chép kết quả.

  1. Thay đổi dữ liệu đầu vào và dự đoán output trước khi chạy.
  2. Bổ sung kiểm tra cho dữ liệu rỗng hoặc không hợp lệ.
  3. Đóng gói logic thành hàm và viết thêm nhiều test case.
Chạy trong Python IDE →