Class vs Instance Variables: The Mutation Trap That Catches Experienced Developers
共享的可變類別屬性會產生不明顯的行為,這種情況經常出現在資深 Python 面試中
class Student:
grades = []
def add_grade(self, grade):
self.grades.append(grade)
alice = Student()
bob = Student()
alice.add_grade(90)
bob.add_grade(85)
print(alice.grades)
print(bob.grades)
Enter fullscreen mode Exit fullscreen mode
你認為這段程式碼會輸出什麼?
輸出結果:
[90, 85]
[90, 85]
Enter fullscreen mode Exit fullscreen mode
兩個學生共用同一個 grades 清單。這就是類別變數的變動陷阱。
為什麼會發生這種情況
類別變數定義在類別物件本身,而非實例上。當你存取 self.grades 時,Python 會先檢查實例的 __dict__。找不到時,才會向上查找類別中的屬性。alice 和 bob 都會找到同一個位於 Student 類別上的清單物件。
.append() 會變動這個共享的清單物件。所有實例都能看到這個變動。
解決方法
class Student:
def __init__(self):
self.grades = [] # instance variable, created fresh for each instance
def add_grade(self, grade):
self.grades.append(grade)
alice = Student()
bob = Student()
alice.add_grade(90)
bob.add_grade(85)
print(alice.grades)
print(bob.grades)
Enter fullscreen mode Exit fullscreen mode
輸出結果:
[90]
[85]
Enter fullscreen mode Exit fullscreen mode
將清單初始化移到 __init__ 中,可為每個實例建立新的清單。
實例變數遮蔽
class Counter:
count = 0
def increment(self):
self.count += 1
c1 = Counter()
c2 = Counter()
c1.increment()
c1.increment()
c2.increment()
print(Counter.count)
print(c1.count)
print(c2.count)
Enter fullscreen mode Exit fullscreen mode
輸出結果:
0
2
1
Enter fullscreen mode Exit fullscreen mode
self.count += 1 等同於 self.count = self.count + 1。讀取 self.count 會找到類別變數(0)。賦值則會在 self 上建立新的實例變數,遮蔽類別變數。類別變數維持為 0。
這與可變類別屬性的情況不同。整數是不可變的,因此 += 會建立新物件並綁定到實例,而不是變動類別層級的物件。
何時適合使用類別變數
類別變數適用於:
所有實例共享的常數
適用於所有實例的配置值
真正追蹤類別範圍狀態的計數器(需小心管理)
class DatabaseConnection:
MAX_CONNECTIONS = 10 # constant — appropriate as class variable
active_connections = 0 # class-wide counter — appropriate if managed carefully
def __init__(self):
if DatabaseConnection.active_connections >= DatabaseConnection.MAX_CONNECTIONS:
raise RuntimeError("Connection limit reached")
DatabaseConnection.active_connections += 1
Enter fullscreen mode Exit fullscreen mode
請注意,修改類別變數時應透過類別名稱而非 self 來進行,以明確表達意圖並避免意外建立實例變數。
PyCodeIt 2.0 正式推出!
我已從頭重建你的終極 Python 面試沙盒。無論你是計算機科學學生、資料科學畢業生,還是準備通過技術面試的開發者,Python 程式碼追蹤都是一項經常被測試卻很少練習的技能。PyCodeIt 專門為彌補這個差距而建,而我們的最新更新改變了一切。
請至 pycodeit.com 練習類別變數追蹤問題。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.