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

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")
Giá trị thực sự nằm ở đâu
Productthân classtax_rate=0.1(class var)applename='apple'banananame='banana'giữtra cứu chungtra cứu chung
Class variable tax_rate sống ở một chỗ trên Product — cả applebanana đề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.

Phạm vi tra cứu — Instance trước, rồi đến class
Product (không gian class variable)
  • tax_rate = 0.1 ← class variable (chia sẻ)
apple (không gian instance variable)
  • name = 'apple' ← instance variable
banana (không gian instance variable)
  • name = 'banana' ← instance variable
Python tìm thuộc tính bằng cách kiểm tra instance trước, rồi quay về class bên ngoài. Từ góc nhìn của instance, class variable hoạt động như một chiếc ô chung ở trên.
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

Thêm một class variable và xác nhận bạn có thể đọc nó từ cả class và từ instance.

① Trong class Product:, thêm một class variable currency = "USD" trên một dòng riêng (trên __init__).

② Viết __init__(self, name, price) gán self.name = nameself.price = price.

③ Tạo apple = Product("apple", 150). Chạy cả print(Product.currency)print(apple.currency) và xác nhận chúng hiển thị cùng một giá trị.

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

Python Editor

Chạy code để xem đầu ra

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.

Mỗi instance sở hữu instance variable riêng
apple(instance)name = 'apple'(bộ nhớ riêng)banana(instance)name = 'banana'(bộ nhớ riêng)giữgiữ
apple.namebanana.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

Dùng id() để cảm nhận rằng instance variable thực sự tách biệt.

① Định nghĩa class Product: với __init__(self, name, price) gán self.nameself.price.

② Tạo apple = Product("apple", 150)banana = Product("banana", 80).

③ Chạy print(apple is banana)print(id(apple.name) == id(banana.name)) và xác nhận cả hai đều False.

Python Editor

Chạy code để xem đầu ra

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)
Cái gì thay đổi trước vs sau khi gán
apple(không có instance var)banana(không có instance var)Producttax_rate=0.1apple.tax_rate=0.05★ mớitax_rate=0.05instance var của applebanana(không có instance var)Producttax_rate=0.1(không đổi)tra cứu chungtra cứutra cứu
Hàng trên (trước): cả applebanana 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.

Tự tay xác nhận rằng self.x = ... tạo ra một instance variable mới.

① Định nghĩa class Product: với class variable tax_rate = 0.1__init__(self, name) gán self.name = name.

② Tạo apple = Product("apple")banana = Product("banana").

③ Sau apple.tax_rate = 0.05, chạy print(apple.tax_rate), print(banana.tax_rate), và print(Product.tax_rate). Xác nhận chỉ apple hiển thị giá trị mới còn các cái khác giữ ở 0.1.

Python Editor

Chạy code để xem đầu ra

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.

Product.x += 1 vs self.x += 1
Product.x+= 1class x tănglên 3tổng tích lũyhoạt độngself.x+= 1mỗi instancesinh x mớiclass x giữở 0cập nhậtkết quảgánkết quả
Gán qua class cho phép mọi người tăng cùng một bộ đếm duy nhất. Với 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)

Tạo một bộ đếm tăng class variable lên 1 mỗi khi tạo instance.

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

② Trong __init__(self, name), sau self.name = name, viết Product.created_count += 1 trên một dòng.

③ Tạo Product("apple"), Product("banana"), và Product("orange") lần lượt, rồi chạy print(Product.created_count) và xác nhận nó hiển thị 3.

Python Editor

Chạy code để xem đầu ra

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.

QUIZ

Kiểm tra kiến thức

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

Câu 1Bạn viết một class variable ở đâu?

Câu 2print cuối cùng xuất ra cái gì?
class P:
n = 10
a = P()
a.n = 99
b = 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__?