Bài 12. Lập trình hướng đối tượng

Lập trình hướng đối tượng (OOP) giúp tổ chức chương trình lớn thành các “vật” có dữ liệu và hành vi. Bài này dạy định nghĩa lớp, thuộc tính, phương thức, property và phương thức đặc biệt.

55 phútTrung cấp
Bạn sẽ học được
  • Định nghĩa lớp với __init__ và phương thức
  • Phân biệt thuộc tính đối tượng và thuộc tính lớp
  • Dùng @property, @classmethod, @staticmethod
  • Viết __str__, __repr__, __eq__
Thuật ngữ cần biết
Lớp (class)
Bản thiết kế mô tả dữ liệu và hành vi.
Đối tượng (instance)
Một thực thể cụ thể tạo từ lớp.
self
Tham chiếu tới chính đối tượng đang xét.
Dunder method
Phương thức đặc biệt dạng __ten__.

Khái niệm và lý thuyết

Lớp gom thuộc tính (dữ liệu) và phương thức (hành vi) lại một chỗ. Mỗi đối tượng có bản dữ liệu riêng nhưng dùng chung phương thức. Bốn nguyên lý: đóng gói, kế thừa, đa hình, trừu tượng.

Python không có private thật; quy ước tên bắt đầu bằng _ là “nội bộ, đừng dùng từ ngoài”. @property cho phép kiểm tra dữ liệu khi gán mà vẫn dùng cú pháp truy cập thuộc tính bình thường.

Lớp đầu tiên

class SinhVien:
    truong = "Đại học Tây Bắc"          # thuộc tính lớp, dùng chung

    def __init__(self, ma: str, ho_ten: str):
        self.ma = ma                     # thuộc tính đối tượng
        self.ho_ten = ho_ten
        self._diem: list[float] = []

    def them_diem(self, d: float) -> None:
        if not 0 <= d <= 10:
            raise ValueError("Điểm phải từ 0 đến 10")
        self._diem.append(d)

    @property
    def diem_tb(self) -> float:
        return sum(self._diem) / len(self._diem) if self._diem else 0.0

    def __str__(self):
        return f"{self.ma} - {self.ho_ten} ({self.diem_tb:.2f})"

sv = SinhVien("SV01", "Lò Văn An")
sv.them_diem(8); sv.them_diem(9)
print(sv)            # SV01 - Lò Văn An (8.50)

Property có setter, classmethod, staticmethod

class NhietDo:
    def __init__(self, c: float):
        self.c = c                       # đi qua setter
    @property
    def c(self): return self._c
    @c.setter
    def c(self, gia_tri):
        if gia_tri < -273.15: raise ValueError("Dưới độ không tuyệt đối")
        self._c = gia_tri
    @property
    def f(self): return self._c * 9 / 5 + 32
    @classmethod
    def tu_f(cls, f): return cls((f - 32) * 5 / 9)   # constructor thay thế
    @staticmethod
    def hop_le(x): return x >= -273.15

Phương thức đặc biệt

Phương thứcĐược gọi khi
__str__print(obj), str(obj)
__repr__Hiển thị trong console, gỡ lỗi
__eq__, __lt__So sánh ==, <
__len__len(obj)
__add__obj1 + obj2
class PhanSo:
    def __init__(self, tu, mau): self.tu, self.mau = tu, mau
    def __add__(self, o): return PhanSo(self.tu * o.mau + o.tu * self.mau, self.mau * o.mau)
    def __repr__(self): return f"{self.tu}/{self.mau}"
print(PhanSo(1, 2) + PhanSo(1, 3))   # 5/6
Mẹo hay

Đặt tên lớp theo CamelCase (SinhVien), tên hàm/biến theo snake_case (them_diem) theo chuẩn PEP 8.

Lưu ý

Quên tham số self ở phương thức gây lỗi “takes 0 positional arguments but 1 was given”.

Thực hành
  1. Viết lớp TaiKhoan có nạp, rút (không cho âm), lịch sử giao dịch.
  2. Viết lớp HinhChuNhat có property dien_tich, chu_vi và __eq__.
  3. Thêm classmethod tu_chuoi("SV01;Lò Văn An") cho SinhVien.
  4. Viết lớp Vector2D hỗ trợ +, -, * số và abs().
Tóm tắt bài học
  • Lớp = thuộc tính + phương thức; self là đối tượng hiện tại.
  • @property kiểm soát truy cập dữ liệu.
  • classmethod tạo constructor thay thế; staticmethod là hàm tiện ích.
  • Dunder method giúp đối tượng dùng như kiểu có sẵn.