Giới thiệu lập trình hướng đối tượng trong Python
Bạn đã từng nghe về OOP (Object-Oriented Programming) nhưng vẫn thấy mơ hồ về khái niệm này? Hay bạn đang tự hỏi tại sao nhiều lập trình viên lại ưa chuộng phong cách lập trình này đến vậy?
Lập trình hướng đối tượng không chỉ là một khái niệm mà còn là một triết lý tổ chức code giúp dự án của bạn trở nên rõ ràng, dễ bảo trì và có thể mở rộng một cách hiệu quả. Thay vì viết code theo kiểu tuần tự truyền thống, OOP giúp bạn tạo ra những “khối xây dựng” có thể tái sử dụng và kết hợp linh hoạt.

Trong bài viết này, tôi sẽ đưa bạn đi từ những khái niệm cơ bản nhất của OOP trong Python đến các kỹ thuật nâng cao, kèm theo những ví dụ thực tế mà bạn có thể áp dụng ngay vào dự án của mình. Chúng ta sẽ khám phá cách khai báo class, tạo object, hiểu về tính kế thừa, đa hình, các hàm đặc biệt và những best practices mà mọi lập trình viên Python nên nắm vững.
Hãy cùng bắt đầu hành trình khám phá thế giới OOP trong Python – một công cụ mạnh mẽ sẽ thay đổi cách bạn nghĩ và viết code!
Khái niệm cơ bản về OOP trong Python
Class và Object là gì?
Để hiểu OOP, bạn cần nắm vững hai khái niệm cốt lõi: Class và Object. Hãy tưởng tượng Class như một bản thiết kế, còn Object là những sản phẩm được tạo ra từ bản thiết kế đó.
Ví dụ, bạn có thể tạo một class XeHoi
như một mẫu thiết kế chung. Từ class này, bạn có thể tạo ra nhiều object khác nhau như xe_toyota
, xe_honda
, mỗi object sẽ có những đặc điểm riêng nhưng vẫn tuân theo cấu trúc chung của class.

class XeHoi:
def __init__(self, hang, mau):
self.hang = hang
self.mau = mau
def chay(self):
return f"Xe {self.hang} màu {self.mau} đang chạy"
# Tạo object từ class
xe_toyota = XeHoi("Toyota", "đỏ")
xe_honda = XeHoi("Honda", "xanh")
Kiểu dữ liệu trong Python đóng vai trò quan trọng trong việc xác định các thuộc tính như hang
và mau
của object trong class.
Thuộc tính và phương thức
Trong OOP, thuộc tính (attributes) là những biến lưu trữ dữ liệu của object, còn phương thức (methods) là những hàm xử lý logic bên trong class. Điều này giúp bạn đóng gói dữ liệu và hành vi liên quan vào cùng một nơi.
Thuộc tính giống như những đặc điểm của object – màu sắc, kích thước, tên gọi. Phương thức giống như những hành động mà object có thể thực hiện – di chuyển, tính toán, hiển thị thông tin.
class NguoiDung:
def __init__(self, ten, tuoi):
self.ten = ten # Thuộc tính
self.tuoi = tuoi # Thuộc tính
def chao_hoi(self): # Phương thức
return f"Xin chào, tôi là {self.ten}"
def tang_tuoi(self): # Phương thức
self.tuoi += 1
return f"{self.ten} bây giờ {self.tuoi} tuổi"
Cách tiếp cận này giúp code của bạn trở nên logic và dễ hiểu hơn. Thay vì có nhiều hàm rời rạc, mọi thứ liên quan đều được tập trung vào class tương ứng.
Hướng dẫn tạo và sử dụng Class & Object trong Python
Viết class cơ bản và khởi tạo object
Việc tạo class trong Python rất đơn giản với từ khóa class
. Hàm __init__
đóng vai trò như constructor, giúp khởi tạo các thuộc tính ban đầu cho object.

class HocSinh:
def __init__(self, ten, lop, diem=[]):
self.ten = ten
self.lop = lop
self.diem = diem if diem else []
def them_diem(self, diem_moi):
self.diem.append(diem_moi)
print(f"Đã thêm điểm {diem_moi} cho {self.ten}")
def tinh_diem_trung_binh(self):
if len(self.diem) == 0:
return 0
return sum(self.diem) / len(self.diem)
# Khởi tạo object
hoc_sinh1 = HocSinh("Nguyễn Văn An", "10A1")
hoc_sinh2 = HocSinh("Trần Thị Bình", "10A2", [8, 7, 9])
Khi tạo object, Python sẽ tự động gọi hàm __init__
và truyền các tham số bạn cung cấp. Mỗi object được tạo sẽ có vùng nhớ riêng, do đó việc thay đổi thuộc tính của object này không ảnh hưởng đến object khác.
Sử dụng thuộc tính và phương thức trong thực tế
Sau khi tạo object, bạn có thể truy cập thuộc tính bằng dấu chấm và gọi phương thức để thực hiện các thao tác cần thiết. Đây là lúc sức mạnh của OOP thực sự thể hiện.
# Thao tác với object hoc_sinh1
print(f"Tên: {hoc_sinh1.ten}")
print(f"Lớp: {hoc_sinh1.lop}")
hoc_sinh1.them_diem(8.5)
hoc_sinh1.them_diem(7.0)
hoc_sinh1.them_diem(9.0)
diem_tb = hoc_sinh1.tinh_diem_trung_binh()
print(f"Điểm trung bình của {hoc_sinh1.ten}: {diem_tb:.2f}")
# Thao tác với object hoc_sinh2
diem_tb2 = hoc_sinh2.tinh_diem_trung_binh()
print(f"Điểm trung bình của {hoc_sinh2.ten}: {diem_tb2:.2f}")

Bạn có thể thấy mỗi object hoạt động độc lập, có dữ liệu riêng nhưng vẫn sử dụng chung các phương thức được định nghĩa trong class. Điều này giúp tiết kiệm bộ nhớ và tạo ra code có tính nhất quán cao.
Các đặc tính nâng cao trong OOP Python
Kế thừa – Tái sử dụng và mở rộng class
Kế thừa (inheritance) là một trong những tính năng mạnh mẽ nhất của OOP, cho phép bạn tạo class mới dựa trên class đã có. Class con sẽ thừa hưởng tất cả thuộc tính và phương thức của class cha, đồng thời có thể thêm tính năng riêng hoặc override các phương thức có sẵn.

class ConNguoi:
def __init__(self, ten, tuoi):
self.ten = ten
self.tuoi = tuoi
def gioi_thieu(self):
return f"Tôi là {self.ten}, {self.tuoi} tuổi"
def di_chuyen(self):
return f"{self.ten} đang đi bộ"
class HocSinh(ConNguoi): # Kế thừa từ ConNguoi
def __init__(self, ten, tuoi, truong):
super().__init__(ten, tuoi) # Gọi constructor của class cha
self.truong = truong
def gioi_thieu(self): # Override phương thức của class cha
return f"Tôi là {self.ten}, {self.tuoi} tuổi, học tại {self.truong}"
def hoc_bai(self): # Phương thức riêng của class con
return f"{self.ten} đang học bài"
class GiaoVien(ConNguoi):
def __init__(self, ten, tuoi, mon_day):
super().__init__(ten, tuoi)
self.mon_day = mon_day
def day_hoc(self):
return f"{self.ten} đang dạy môn {self.mon_day}"
Kế thừa giúp bạn tránh lặp lại code và tạo ra cấu trúc phân cấp logic trong dự án. Khi cần thay đổi logic chung, bạn chỉ cần sửa ở class cha, tất cả class con sẽ được cập nhật tự động.
Vòng lặp trong Python cũng có thể kết hợp linh hoạt với các cấu trúc OOP để duyệt và xử lý dữ liệu trong object.
Đa hình và đóng gói
Đa hình (polymorphism) cho phép nhiều class khác nhau có thể có phương thức cùng tên nhưng hoạt động khác nhau. Điều này tạo ra tính linh hoạt cao trong thiết kế chương trình.
class DongVat:
def phat_ra_tieng_keu(self):
pass
class Cho(DongVat):
def phat_ra_tieng_keu(self):
return "Gâu gâu"
class Meo(DongVat):
def phat_ra_tieng_keu(self):
return "Meo meo"
class Bo(DongVat):
def phat_ra_tieng_keu(self):
return "Ò ò"
# Sử dụng đa hình
dong_vat_list = [Cho(), Meo(), Bo()]
for dong_vat in dong_vat_list:
print(dong_vat.phat_ra_tieng_keu())
Đóng gói (encapsulation) giúp bạn bảo vệ dữ liệu bằng cách sử dụng các biến private (bắt đầu bằng dấu gạch dưới). Mặc dù Python không có từ khóa private thực sự, quy ước này giúp báo hiệu rằng thuộc tính không nên được truy cập trực tiếp từ bên ngoài.

Các hàm đặc biệt và cách sử dụng trong Python
Các hàm khởi tạo và đại diện (__init__, __str__)
Python cung cấp nhiều hàm đặc biệt (magic methods) giúp bạn tùy chỉnh hành vi của class. Hai hàm quan trọng nhất mà bạn cần biết là __init__
và __str__
.
Hàm __init__
được gọi tự động khi tạo object, giúp khởi tạo các thuộc tính ban đầu. Hàm __str__
định nghĩa cách hiển thị object dưới dạng chuỗi khi sử dụng hàm print()
.
class SanPham:
def __init__(self, ten, gia, so_luong):
self.ten = ten
self.gia = gia
self.so_luong = so_luong
def __str__(self):
return f"Sản phẩm: {self.ten} - Giá: {self.gia}đ - Số lượng: {self.so_luong}"
def tinh_tong_gia_tri(self):
return self.gia * self.so_luong
# Sử dụng
san_pham = SanPham("Laptop Dell", 15000000, 3)
print(san_pham) # Tự động gọi __str__
print(f"Tổng giá trị: {san_pham.tinh_tong_gia_tri():,}đ")
Các hàm magic phổ biến khác cần biết
Bên cạnh __init__
và __str__
, còn có nhiều hàm magic khác giúp bạn tùy chỉnh hành vi của class một cách chi tiết. Hàm __repr__
tương tự __str__
nhưng thường được sử dụng cho mục đích debug. Hàm __del__
được gọi khi object bị hủy. Hàm __eq__
định nghĩa cách so sánh hai object.

class TheTinDung:
def __init__(self, chu_the, so_tai_khoan, so_du=0):
self.chu_the = chu_the
self.so_tai_khoan = so_tai_khoan
self.so_du = so_du
def __str__(self):
return f"Thẻ của {self.chu_the} - Số dư: {self.so_du:,}đ"
def __repr__(self):
return f"TheTinDung('{self.chu_the}', '{self.so_tai_khoan}', {self.so_du})"
def __eq__(self, other):
if isinstance(other, TheTinDung):
return self.so_tai_khoan == other.so_tai_khoan
return False
def __del__(self):
print(f"Thẻ tín dụng của {self.chu_the} đã được hủy")
# Sử dụng các magic methods
the1 = TheTinDung("Nguyễn Văn A", "123456789", 1000000)
the2 = TheTinDung("Trần Thị B", "123456789", 500000)
print(the1) # Gọi __str__
print(repr(the1)) # Gọi __repr__
print(the1 == the2) # Gọi __eq__
Các vấn đề thường gặp khi học OOP Python
Lỗi hay gặp khi khai báo class và tạo object
Một trong những lỗi phổ biến nhất mà các lập trình viên mới gặp phải là quên sử dụng self
trong các phương thức của class. Tham số self
là cách Python xác định object nào đang được thao tác trong phương thức.
# SAI - Quên self
class HinhChuNhat:
def __init__(dai, rong): # Thiếu self
dai = dai # Không gán được vào thuộc tính
rong = rong
# ĐÚNG - Có self
class HinhChuNhat:
def __init__(self, self, dai, rong):
self.dai = dai
self.rong = rong
def tinh_dien_tich(self): # Phải có self
return self.dai * self.rong

Một lỗi khác là nhầm lẫn giữa thuộc tính class và thuộc tính instance. Thuộc tính class được chia sẻ giữa tất cả object, còn thuộc tính instance thì mỗi object có một bản riêng.
class DemSoLuong:
so_luong_chung = 0 # Thuộc tính class
def __init__(self, ten):
self.ten = ten # Thuộc tính instance
DemSoLuong.so_luong_chung += 1
# Mỗi object có tên riêng nhưng cùng chia sẻ số lượng chung
obj1 = DemSoLuong("Object 1")
obj2 = DemSoLuong("Object 2")
print(f"Số lượng object: {DemSoLuong.so_luong_chung}") # Kết quả: 2
Khó hiểu tính kế thừa và đa hình
Nhiều người gặp khó khăn khi hiểu cách thức hoạt động của kế thừa, đặc biệt là việc sử dụng super()
và method resolution order (MRO). Hãy nhớ rằng super()
giúp bạn gọi phương thức của class cha mà không cần chỉ định tên cụ thể.
class Xe:
def __init__(self, hang):
self.hang = hang
print(f"Khởi tạo xe {hang}")
class XeOto(Xe):
def __init__(self, hang, so_cho_ngoi):
super().__init__(hang) # Gọi constructor của class Xe
self.so_cho_ngoi = so_cho_ngoi
print(f"Xe oto {hang} có {so_cho_ngoi} chỗ ngồi")
class XeDien(XeOto): # Kế thừa nhiều cấp
def __init__(self, hang, so_cho_ngoi, dung_luong_pin):
super().__init__(hang, so_cho_ngoi)
self.dung_luong_pin = dung_luong_pin
print(f"Pin có dung lượng {dung_luong_pin} kWh")
# Tạo object sẽ gọi tất cả constructor theo thứ tự
xe_dien = XeDien("Tesla", 5, 100)
Best practices khi lập trình hướng đối tượng trong Python
Khi làm việc với OOP trong Python, việc tuân thủ các best practices sẽ giúp code của bạn trở nên chuyên nghiệp và dễ bảo trì hơn. Đầu tiên, luôn nhớ sử dụng self
một cách rõ ràng trong mọi phương thức instance. Điều này không chỉ là yêu cầu về cú pháp mà còn giúp code dễ đọc hơn.

Việc đặt tên class, method và thuộc tính cần tuân thủ chuẩn PEP8. Class nên sử dụng CapitalizedWords (PascalCase), trong khi method và thuộc tính nên dùng lowercase_with_underscore. Điều này giúp code nhất quán và dễ đọc cho mọi lập trình viên Python.
# Đặt tên chuẩn PEP8
class QuanLyNhanVien: # Class dùng PascalCase
def __init__(self, ten_nhan_vien, luong_co_ban): # Method/thuộc tính dùng snake_case
self.ten_nhan_vien = ten_nhan_vien
self.luong_co_ban = luong_co_ban
def tinh_luong_thang(self, he_so_thuong=1.0):
return self.luong_co_ban * he_so_thuong
Tránh lạm dụng kế thừa quá sâu vì điều này có thể làm cho code trở nên khó hiểu và khó bảo trì. Thường thì cấu trúc kế thừa không nên quá 3-4 cấp độ. Thay vào đó, hãy sử dụng composition (tổng hợp) khi có thể.
Sử dụng encapsulation để bảo vệ dữ liệu quan trọng bằng cách đặt tên thuộc tính bắt đầu với dấu gạch dưới. Mặc dù Python không có private thực sự, quy ước này giúp báo hiệu rằng thuộc tính không nên được truy cập trực tiếp từ bên ngoài.
Cuối cùng, luôn viết code theo nguyên tắc modular, tách biệt trách nhiệm rõ ràng. Mỗi class nên có một mục đích cụ thể, mỗi method nên thực hiện một nhiệm vụ duy nhất. Điều này giúp code dễ test, dễ debug và dễ mở rộng.

Kết luận
OOP trong Python thực sự là một công cụ mạnh mẽ giúp bạn tổ chức và quản lý code một cách hiệu quả, tạo ra những dự án có thể mở rộng và bảo trì dễ dàng. Qua bài viết này, chúng ta đã cùng khám phá từ những khái niệm cơ bản như class và object, đến những kỹ thuật nâng cao như kế thừa, đa hình và các hàm đặc biệt.
Việc áp dụng đúng các nguyên tắc OOP không chỉ giúp code của bạn trở nên chuyên nghiệp mà còn tạo ra nền tảng vững chắc cho những dự án lớn hơn trong tương lai. Hãy nhớ rằng OOP không phải là mục tiêu cuối cùng mà là phương tiện giúp bạn giải quyết vấn đề một cách tốt hơn.

Bây giờ là lúc để bạn áp dụng những kiến thức đã học vào thực tế. Hãy bắt đầu với những dự án nhỏ, luyện tập tạo class và object, thử nghiệm với kế thừa và các tính năng nâng cao khác. Qua mỗi dự án, bạn sẽ hiểu sâu hơn về sức mạnh của OOP và cách áp dụng nó một cách hiệu quả.
Đừng ngần ngại tham gia các cộng đồng lập trình viên Python, đặt câu hỏi và chia sẻ kinh nghiệm. Việc học hỏi từ những lập trình viên khác sẽ giúp bạn nâng cao kỹ năng nhanh chóng và tránh được những sai lầm phổ biến. Chúc bạn thành công trên con đường chinh phục OOP trong Python!
Ứng dụng của Python được phát triển rộng rãi trong các lĩnh vực thực tế, giúp bạn thấy rõ lợi ích khi nắm vững lập trình hướng đối tượng.
Chia sẻ Tài liệu học Python