博客 / 詳情

返回

別再浪費內存了:Python __slots__ 機制深入解析

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

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.