Câu 1Decorator nào bạn dùng để định nghĩa một static method?
Instance, Class và Static method — Ba loại phương thức
Học ba loại phương thức của Python — instance, class, và static — kèm sơ đồ. Đi qua việc dùng self / cls / không có đối số đầu và khi nào chọn cái nào.
Lần trước ta đã bao quát sự khác biệt giữa class và instance variable. Mọi phương thức ta đã viết đến giờ ở dạng def method(self, ...) đều là instance method, nhưng class trong Python còn có hai loại nữa — class method và static method. Lần này ta sẽ sắp xếp ba loại và cách chọn giữa chúng.
Có ba loại phương thức
Phương thức trong một class Python rơi vào ba loại, phân biệt bởi cái gì chúng nhận làm đối số đầu tiên:
- Instance method — tham số đầu self. Nhận chính instance.
- Class method — tham số đầu cls. Nhận chính class. Trang trí bằng @classmethod.
- Static method — không có tham số đầu. Không nhận self cũng không cls. Trang trí bằng @staticmethod.
Loại quyết định cái gì mỗi phương thức có thể chạm tới. Chỉ instance method mới có thể chạm vào instance variable self.x. Class method là cách bạn làm việc với class variable từ phía class, và static method dành cho logic thuần không chạm vào cái nào cả.
Instance method — Nhận self
Dạng def method(self, ...) mà bạn đã viết suốt là một instance method. Tham số đầu self được điền tự động với instance mà phương thức được gọi từ đó, nên bạn có thể chạm tới instance variable dưới dạng self.name.
Khi bạn viết apple.show(), Python bên trong dịch nó thành Product.show(apple). Bạn có thể nghĩ về phương thức như "hàm nhận một instance làm đối số đầu tiên."
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def total_with_tax(self, tax_rate): # instance method
return int(self.price * (1 + tax_rate))
apple = Product("apple", 150)
print(apple.total_with_tax(0.1)) # 165
# Bên trong tương đương cái này
print(Product.total_with_tax(apple, 0.1)) # 165
__init__ gán apple.price = 150 → ② gọi apple.method(0.1) đặt apple vào self → ③ self.price đọc giá trị gán trên instance.Class method — @classmethod và cls
Một class method được định nghĩa với decorator @classmethod, với cls làm tham số đầu (một tên theo quy ước). cls tự động giữ chính class — trong trường hợp này là blueprint Product.
Dùng class method khi bạn muốn đọc hoặc ghi class variable hoặc tạo và trả về một instance (factory). Bạn cũng có thể gọi chúng qua một instance (apple.method()), nhưng gọi chúng trên class dưới dạng Product.method() là cách tiêu chuẩn.
class Product:
total_count = 0 # class variable
def __init__(self, name, price):
self.name = name
self.price = price
Product.total_count += 1
@classmethod # ① trả về một class variable
def get_total(cls):
return f"{cls.total_count} sản phẩm đã đăng ký"
@classmethod # ② tạo và trả về một instance (factory)
def from_csv_row(cls, row):
name, price = row.split(",")
return cls(name, int(price))
Product("apple", 150)
Product("banana", 80)
print(Product.get_total()) # 2 sản phẩm đã đăng ký
orange = Product.from_csv_row("orange,120")
print(orange.name, orange.price) # orange 120
get_total(cls) (trang trí bằng @classmethod) dưới dạng Product.get_total() tự động đặt chính class Product vào cls. Từ đó bạn có thể chạm vào class variable dưới dạng cls.total_count.def total_with_tax(self, tax_rate):- Gọi:
apple.total_with_tax(0.1) - →
selftrở thànhapple - Chạm tới instance variable self.price
- Trang trí bằng
@classmethod def get_total(cls):- Gọi:
Product.get_total()(từ class) - →
clstrở thànhProduct - Chạm tới class variable cls.total_count
Static method — @staticmethod
Một static method được định nghĩa với @staticmethod và không nhận self cũng không cls. Nó là "một hàm thông thường sống trong class" — hoàn thành công việc chỉ dùng đối số, không có truy cập đến instance hay class variable.
Nó hợp với các trường hợp công việc liên quan đến class nhưng không phụ thuộc vào instance hay trạng thái class cụ thể — kiểm tra đầu vào, định dạng, tính toán thuần dùng hằng số. Dùng nó để giữ các utility function bên cạnh class liên quan thay vì viết chúng như hàm độc lập.
class Product:
def __init__(self, name, price):
if not Product.is_valid_price(price):
raise ValueError(f"giá không hợp lệ: {price}")
self.name = name
self.price = price
@staticmethod
def is_valid_price(price): # không self, không cls
return 0 <= price <= 1_000_000
print(Product.is_valid_price(150)) # True
print(Product.is_valid_price(-1)) # False
# Product("apple", -1) # ValueError: giá không hợp lệ: -1
Static method hoạt động từ class hay instance
Cả Product.is_valid_price(150) và apple.is_valid_price(150) đều chạy như nhau. Nhưng vì phương thức thực sự không dùng instance, gọi nó trên class làm ý đồ rõ hơn. Viết apple.is_valid_price(...) khiến người đọc dừng lại và tự hỏi "có phụ thuộc vào trạng thái của apple không?" — nên tránh.
Chọn giữa ba loại
Dùng cái nào có thể quyết định một cách máy móc dựa trên "phương thức cần chạm tới cái gì."
- Chạm vào instance variable (self.x) → instance method
- Chỉ chạm vào class variable (cls.x) → class method
- Không chạm cái nào (chỉ dùng đối số) → static method
Khi nghi ngờ: trước hết kiểm tra xem bạn có chạm vào instance variable không. Nếu không, kiểm tra class variable. Nếu cả hai đều không, chọn static. Thứ tự đó sẽ không dẫn bạn lạc.
self.x không? Tiếp: bạn có xử lý class variable không? Nếu cả hai đều không, static là lựa chọn đúng.| Loại | Decorator | Tham số đầu | Ứng dụng tiêu biểu |
|---|---|---|---|
| Instance method | không có | self | Phương thức thông thường đọc/ghi self.x |
| Class method | @classmethod | cls | Đọc/ghi class variable, factory |
| Static method | @staticmethod | không có | Validation, định dạng, logic thuần |
Với bài này bạn đã bao quát ba loại phương thức — instance / class / static — và cách chọn giữa chúng. Điều đó hoàn thành bộ công cụ cốt lõi của class, instance, thuộc tính, và phương thức trong Python. Từ đây, bạn có thể chuyển sang ba trụ cột của lập trình hướng đối tượng: đóng gói (kiểm soát khả năng nhìn thấy của thuộc tính), kế thừa (mang theo khả năng từ một class khác), và đa hình (cùng tên phương thức hành xử khác nhau theo kiểu).
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2Phương thức nào là phù hợp tự nhiên nhất cho @classmethod?
Câu 3Phương thức nào có thể được gọi mà không tạo instance? (Chọn câu trả lời đơn lẻ phù hợp nhất nếu nhiều đáp án đúng.)