Câu 1Bạn viết một class variable ở đâu?
Class variable vs Instance variable — Giá trị thực sự nằm ở đâu
Hướng dẫn nhiều sơ đồ về class variable vs instance variable trong Python. Xem cái gì được chia sẻ, cái gì riêng cho từng instance, và self.x = ... thực sự đang làm gì.
Lần trước ta đã tổng kết việc dùng __init__ thực tế — đối số bắt buộc, đối số mặc định — và gặp __del__ đối ứng. Lần này, ta sẽ đào sâu vào hai loại biến trong một class — class variable và instance variable — và xem self.x = ... thực sự làm gì ở mức bộ nhớ.
Hai loại biến
Biến mà bạn viết trong một class Python rơi vào hai loại chính:
- Class variable — viết trực tiếp dưới class Product:. Được chia sẻ bởi mọi instance.
- Instance variable — viết dưới dạng self.x = ... bên trong một phương thức. Được sở hữu bởi từng instance riêng lẻ.
Cả hai đều được đọc bằng cùng cú pháp dấu chấm apple.name, nên bạn không thể phân biệt chúng bằng mắt. Đó chính xác là lý do bạn cần có chủ đích về "giá trị thực sự nằm ở đâu."
class Product:
tax_rate = 0.1 # class variable (chia sẻ bởi tất cả)
def __init__(self, name):
self.name = name # instance variable (theo từng instance)
apple = Product("apple")
banana = Product("banana")
tax_rate sống ở một chỗ trên Product — cả apple và banana đều nhìn vào cùng một chỗ. Instance variable name được lưu trong bộ nhớ riêng của mỗi instance.Class variable — Một giá trị chia sẻ cho tất cả
Viết một class variable trực tiếp dưới class Product:, như tax_rate = 0.1. Phần quan trọng là nó sống bên ngoài bất kỳ phương thức nào. Class tự sở hữu một bản, và mọi instance được tạo từ class đó đều dùng chung.
Bạn có thể đọc nó dưới dạng Product.tax_rate (qua class) hoặc apple.tax_rate (qua instance). Đối với cái sau, Python tra cứu theo thứ tự này: kiểm tra instance trước, rồi quay về class.
- tax_rate = 0.1 ← class variable (chia sẻ)
- name = 'apple' ← instance variable
- name = 'banana' ← instance variable
class Product:
tax_rate = 0.1 # class variable (chia sẻ bởi tất cả)
def __init__(self, name, price):
self.name = name # instance variable (theo từng instance)
self.price = price
apple = Product("apple", 150)
banana = Product("banana", 80)
print(Product.tax_rate) # 0.1 (qua class)
print(apple.tax_rate) # 0.1 (cũng đọc được qua instance)
print(banana.tax_rate) # 0.1
# Cùng vị trí bộ nhớ
print(id(apple.tax_rate) == id(Product.tax_rate)) # True
Instance variable — Sở hữu bởi từng instance
Một instance variable được tạo vào khoảnh khắc bạn viết self.x = ... bên trong một phương thức, dưới dạng thuộc tính riêng cho instance đó. Phổ biến nhất bạn khởi tạo chúng trong __init__ bằng kiểu self.name = name, nhưng viết self.x = ... trong bất kỳ phương thức nào khác cũng tạo một instance variable mới vào khoảnh khắc đó.
Vì mỗi instance lưu instance variable trong bộ nhớ riêng, thay đổi apple.name không ảnh hưởng gì đến banana.name.
apple.name và banana.name sống ở những vị trí bộ nhớ khác nhau. Ghi đè apple.name không ảnh hưởng đến banana.name.class Product:
def __init__(self, name, price):
self.name = name
self.price = price
apple = Product("apple", 150)
banana = Product("banana", 80)
print(apple.name, apple.price) # apple 150
print(banana.name, banana.price) # banana 80
# id() cho thấy chúng trỏ đến chỗ khác nhau
print(id(apple.name) == id(banana.name)) # False
self.x = ... tạo một instance variable
Giả sử Product có class variable tax_rate = 0.1, và bạn viết apple.tax_rate = 0.05 cho một instance cụ thể — chuyện gì xảy ra?
Câu trả lời: class variable không bị chạm vào, và một instance variable tax_rate mới hoàn toàn được tạo trên apple. Từ đó trở đi, apple.tax_rate trả về 0.05 vì Python tìm thấy nó trên instance trước và không bao giờ quay về class. banana hoàn toàn không bị ảnh hưởng.
class Product:
tax_rate = 0.1
def __init__(self, name, price):
self.name = name
self.price = price
apple = Product("apple", 150)
banana = Product("banana", 80)
# Cho riêng apple một thuế suất mới
apple.tax_rate = 0.05
print(apple.tax_rate) # 0.05 (instance variable của apple)
print(banana.tax_rate) # 0.1 (quay về class variable)
print(Product.tax_rate) # 0.1 (class variable không đổi)
apple và banana cùng tra cứu tax_rate=0.1 trên class. Hàng dưới (sau apple.tax_rate = 0.05): một instance variable mới ra đời trên apple, và chỉ apple đọc từ nó. banana và class không bị động đến.Đừng nhầm với "cập nhật class variable qua instance"
apple.tax_rate = 0.05 thoạt nhìn giống như "cập nhật class variable", nhưng thực ra nó chỉ tạo một instance variable mới trên apple. Để thực sự thay đổi class variable chung, bạn cần gán qua class: Product.tax_rate = 0.05.
Khi bạn muốn cập nhật class variable
Khi bạn cần cập nhật trạng thái mà class đang giữ cho mọi người — như "thay đổi thuế suất cho mọi instance cùng lúc" hoặc "theo dõi tổng số sản phẩm đã tạo" — bạn gán qua class variable.
Dưới đây, Product.created_count sống trên chính class và đại diện cho "số sản phẩm đã tạo cho đến nay." Viết Product.created_count += 1 bên trong __init__ cập nhật cùng một chỗ duy nhất bất kể instance nào đang được tạo, nên tổng tích lũy duy trì đúng. Nếu bạn viết self.created_count += 1 thay vào, mỗi instance sẽ có biến riêng, đánh bại mục đích đếm ban đầu.
self.x += 1, mỗi instance kết thúc với bản sao riêng, và bộ đếm chung giữ ở 0.class Product:
created_count = 0 # class variable: tổng tích lũy sản phẩm
def __init__(self, name):
self.name = name
Product.created_count += 1 # cập nhật qua class
apple = Product("apple")
banana = Product("banana")
orange = Product("orange")
print(Product.created_count) # 3
print(apple.created_count) # 3 (cũng đọc được qua instance)
Ta đã sắp xếp hai loại biến trong một class, xác nhận rằng self.x = ... luôn tạo một instance variable mới, rằng instance variable che class variable khi có mặt, và rằng trạng thái chung được cập nhật qua class.
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2print cuối cùng xuất ra cái gì?class P: n = 10a = P()a.n = 99b = P()print(b.n)
Câu 3Để đếm class variable count qua mọi instance, bạn nên viết gì bên trong __init__?