Python 物件屬性預設儲存在字典中,帶來彈性的同時也消耗了大量記憶體,尤其在物件數量龐大的情況下。slots
機制允許預先定義物件屬性,Python 會使用更緊湊的資料結構儲存,避免建立字典,進而節省記憶體並提升效能。這對於需要大量物件的應用程式,例如機器學習模型訓練、資料處理管道或高併發伺服器,尤其有效。然而,slots
也存在一些限制,例如無法動態新增屬性、繼承時需要特別注意 __slots__
的定義等。
Python 的 slots 機制:節省記憶體與效能最佳化的利器
slots 的核心概念與應用場景
在 Python 中,每個物件的屬性都儲存在一個字典 (dict
) 中。這提供了高度的彈性,允許動態新增或修改屬性。然而,當物件數量龐大時,這也會造成記憶體浪費和效能下降。slots
機制則提供了一種解決方案,它允許我們預先定義物件的屬性集合,避免建立額外的字典,從而節省記憶體並提升效能。slots
特別適用於需要建立大量物件的應用程式,例如機器學習、資料處理管道或高併發伺服器。
slots 的內部機制:靜態記憶體組態
當我們在類別中定義 __slots__
時,Python 會為該類別的每個例項分配一個緊湊的結構,而不是建立一個 dict
。這個結構包含預先定義的屬性,其記憶體空間是靜態分配的。讓我們來看一個例子:
class Normal:
def __init__(self, x, y):
self.x = x
self.y = y
class Slotted:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
在 Normal
類別中,每個物件都會擁有一個 dict
來儲存 x
和 y
的值。而在 Slotted
類別中,x
和 y
的記憶體位置是預先分配好的,不再需要額外的 dict
。
內容解密:
Normal
類別的例項會在建立時動態組態一個字典,用於儲存屬性,這會造成記憶體額外開銷。而 Slotted
類別則透過 __slots__
預先定義了屬性,Python 會直接組態固定大小的記憶體空間給這些屬性,避免了字典的建立,因此節省了記憶體。
slots 的限制與注意事項
使用 slots
會帶來一些限制:
無法動態新增屬性: 一旦定義了
__slots__
,就無法再為物件新增不在__slots__
中定義的屬性。嘗試新增未定義的屬性會引發AttributeError
。繼承的影響: 如果父類別使用了
__slots__
,子類別必須也定義__slots__
才能新增自己的屬性。否則,子類別將無法新增任何屬性。如果父類別沒有定義__slots__
,則子類別可以正常新增屬性。多重繼承的情況下,所有父類別都必須使用slots
,否則會造成錯誤。弱參照 (weakref): 預設情況下,使用
slots
的類別不支援弱參照。如果需要使用弱參照,必須將'__weakref__'
加入__slots__
列表中。序列化: 使用
slots
的類別可能會影響序列化 (例如使用pickle
模組)。因為沒有dict
,標準的序列化機制可能無法正常運作。這時需要手動實作__getstate__
和__setstate__
方法來控制序列化過程。
slots 與 dataclass 的整合
從 Python 3.10 開始,@dataclass
裝飾器支援 slots=True
引數,方便我們建立使用 slots
的資料類別,同時保留 dataclass
的簡潔語法。
from dataclasses import dataclass
@dataclass(slots=True)
class Point:
x: int
y: int
這結合了 dataclass
的自動產生 __init__
, __repr__
等方法的優點,以及 slots
節省記憶體的優點。
內容解密:
@dataclass(slots=True)
將 slots
的功能與 dataclass
的便利性結合,簡化了程式碼撰寫,並同時獲得記憶體最佳化的效益。
實務應用案例:
以下是一些 slots
在實際應用中的例子:
1. 機器學習/資料處理管道: 在處理大量資料時,每個資料點都可以表示為一個物件。使用 slots
可以顯著減少記憶體消耗。
class DataPoint:
__slots__ = ('id', 'timestamp', 'value')
def __init__(self, id, timestamp, value):
self.id = id
self.timestamp = timestamp
self.value = value
# 生成大量資料點
data_points = [DataPoint(i, 1627845123 + i, i * 0.5) for i in range(1_000_000)]
2. 伺服器應用程式: 在高併發的伺服器應用程式中,每個客戶端連線都可以表示為一個物件。使用 slots
可以減少每個連線物件的記憶體佔用,從而提高伺服器處理能力。
3. 快取和物件池: 在快取或物件池系統中,物件會被頻繁地建立和銷毀。使用 slots
可以減少記憶體碎片並加快垃圾回收。
內容解密:
以上三個例子都說明瞭在需要大量物件的情況下,使用 slots
可以有效減少記憶體佔用,提升效能。 資料點、客戶端連線和快取專案這些物件通常具有固定數量的屬性,因此非常適合使用 slots
機制。