Python 對象的靈活性大家都知道,可以隨時給對象添加屬性:
class User:
pass
u = User()
u.name = "Alice"
u.age = 30
但這種靈活性的代價也很大,每個普通 Python 對象都有個 __dict__ 字典來存儲屬性,對象一多內存開銷就上來了,這時候 __slots__ 就派上用場。
slots 到底在幹什麼
__slots__ 讓你提前聲明類會用到哪些屬性:
class User:
__slots__ = ["name", "age"]
def __init__(self, name, age):
self.name = name
self.age = age
這樣做之後對象就不會再創建 __dict__ 了。屬性存儲變成靜態的,查找和賦值都快了,尤其是創建大量實例時內存佔用能降不少。
底層存儲機制的差異
普通對象把每個屬性當作 __dict__ 裏的鍵值對訪問屬性就要做哈希查找。而用了 __slots__ 之後每個屬性在內存裏有個固定位置,訪問變成了直接的數組索引操作省掉了字典查找的開銷。
説白了就是把 Python 對象存儲搞得像 C 的結構體一樣緊湊。
內存能省多少
看個對比:
class Normal:
def __init__(self):
self.a = 1
self.b = 2
class Slotted:
__slots__ = ["a", "b"]
def __init__(self):
self.a = 1
self.b = 2
如果要創建幾百萬個實例,用 __slots__ 的版本能少用 50-70% 的內存。
屬性訪問快主要是因為:省掉了字典查找、不用算哈希值、也沒有額外的內存間接訪問,屬性訪問時間一般能減少 20-40%。
使用限制
__slots__ 也不是完美的,有些限制得注意。
最明顯的是不能隨便加屬性了:
u = User()
u.address = "NYC" # ❌ AttributeError
繼承的時候也麻煩,子類得定義自己的 __slots__,而且混用帶slots和不帶slots的類要小心。多重繼承更復雜只有slots名不衝突才行。
另外默認不支持弱引用,要用的話得在 __slots__ 裏顯式加上 __weakref__。
什麼場景適合用
幾個典型場景:處理大數據集時有幾百萬個對象;科學計算裏的輕量數據結構、遊戲引擎裏的實體和粒子系統等等,總之就是那些對內存和速度敏感的地方。
一些實用技巧
配合類型提示用能讓代碼更清晰。可以和 @property 裝飾器結合,該靈活的地方還是保持靈活。空類直接用 __slots__ = () 把開銷降到最低。
總結
__slots__ 就是讓你用靈活性換內存效率和更快的屬性訪問。對於高性能場景來説這是個必須掌握的優化手段。
就算項目暫時不缺內存,理解 __slots__ 本身也很有價值。它能讓你明白 Python 對象是怎麼存屬性的、屬性查找為什麼有成本、Python 在靈活性和效率之間怎麼權衡。
這些知識對系統設計、性能調優、排查問題都有幫助。
https://avoid.overfit.cn/post/c18314c17e0047358cb13c0a990067ae
作者:Elshad Karimov