Học bằng cách đọc theo thứ tự

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 methodstatic 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ả.

Cái gì ba loại phương thức có thể chạm tới
Instancemethod (self)instance vars+ class vars+ argsClassmethod (cls)class vars+ argsStaticmethod (none)chỉ argschạm tớichạm tớichạm tới
Instance method chạm tới instance variable + class variable + đối số — mọi thứ. Class method chạm tới class variable + đối số. Static method chỉ chạm tới đối số.

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
Cách self cho phép bạn chạm vào instance variable
__init__self.price = 150appleprice = 150apple.total_with_tax(0.1)self nhậnappleself.price→ 150①gán②gọi③đọc
__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
Cách class method hoạt động
@classmethoddecoratordef get_total( cls):Product.get_total()cls = Product(class)cls.total_count(class var)khai báoautoqua tra cứu
Gọi 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.
self vs cls
Instance method
  • def total_with_tax(self, tax_rate):
  • Gọi: apple.total_with_tax(0.1)
  • self trở thành apple
  • Chạm tới instance variable self.price
Class method
  • Trang trí bằng @classmethod
  • def get_total(cls):
  • Gọi: Product.get_total() (từ class)
  • cls trở thành Product
  • Chạm tới class variable cls.total_count
self là "instance bạn gọi từ đó"; cls là "chính class". Khi công việc thuần túy về class variable, đi qua cls làm rõ ý đồ hơn.

Viết một class method get_total trả về "số sản phẩm đã tạo cho đến nay."

① Trong class Product:, thêm class variable total_count = 0.

② Trong __init__(self, name, price), sau self.name = name / self.price = price, viết Product.total_count += 1.

③ Định nghĩa get_total trang trí bằng @classmethodtham số đầu cls, trả về cls.total_count.

④ Tạo Product("apple", 150)Product("banana", 80), rồi print(Product.get_total()) và xác nhận nó hiển thị 2.

(Nếu chạy đúng, phần giải thích sẽ hiện ra.)

Python Editor

Chạy code để xem đầu ra

Static method — @staticmethod

Một static method được định nghĩa với @staticmethodkhô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)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.

Viết một static method để kiểm tra giá.

① Trong class Product:, định nghĩa is_valid_price(price) như một @staticmethod trả về 0 <= price <= 1_000_000.

② Ở đầu __init__(self, name, price), viết if not Product.is_valid_price(price): raise ValueError("giá không hợp lệ") trước khi gán self.name / self.price.

③ Xác nhận Product("apple", 150) thành công và Product("sea_urchin", -1) thất bại với ValueError bằng cách bọc nó trong try / except.

Python Editor

Chạy code để xem đầu ra

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.

Luồng quyết định cho loại phương thức
chạmself.x?Instancemethodchạmcls.x?Classmethodkhông chạmcái nàoStaticmethodlogic thuầnsống trong classKhôngKhôngdùng
Nhánh đầu: bạn có chạm vào instance variable 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ạiDecoratorTham số đầuỨng dụng tiêu biểu
Instance methodkhông cóselfPhương thức thông thường đọc/ghi self.x
Class method@classmethodclsĐọc/ghi class variable, factory
Static method@staticmethodkhông cóValidation, định dạng, logic thuần

Đặt cả ba loại phương thức trong một class và cảm nhận sự khác biệt.

① Định nghĩa class Product: với class variable total_count = 0__init__(self, name, price). Ở đầu __init__, raise ValueError nếu Product.is_valid_price(price) thất bại; nếu không gán self.name = name / self.price = price và chạy Product.total_count += 1.

② Thêm một instance method total_with_tax(self, tax_rate) trả về int(self.price * (1 + tax_rate)).

③ Thêm một class method get_total(cls) trang trí bằng @classmethod trả về cls.total_count.

④ Thêm một static method is_valid_price(price) trang trí bằng @staticmethod trả về 0 <= price <= 1_000_000.

⑤ Tạo apple = Product("apple", 150)print cả apple.total_with_tax(0.1)Product.get_total().

Python Editor

Chạy code để xem đầu ra

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).

QUIZ

Kiểm tra kiến thức

Hãy trả lời từng câu hỏi một.

Câu 1Decorator nào bạn dùng để định nghĩa một static method?

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.)