簡體   English   中英

PEP 412 是否使 __slots__ 變得多余?

[英]Does PEP 412 make __slots__ redundant?

在 Python 3.3 中實現的PEP 412引入了改進的屬性字典處理,有效地減少了類實例的內存占用。 __slots__是為同樣的目的而設計的,那么再使用__slots__有什么意義嗎?

為了自己找出答案,我運行了以下測試,但結果沒有多大意義:

class Slots(object):
    __slots__ = ['a', 'b', 'c', 'd', 'e']
    def __init__(self):
        self.a = 1
        self.b = 1
        self.c = 1
        self.d = 1
        self.e = 1  

class NoSlots(object):
    def __init__(self):
        self.a = 1
        self.b = 1
        self.c = 1
        self.d = 1
        self.e = 1

Python 3.3 結果:

>>> sys.getsizeof([Slots() for i in range(1000)])
Out[1]: 9024
>>> sys.getsizeof([NoSlots() for i in range(1000)])
Out[1]: 9024

Python 2.7 結果:

>>> sys.getsizeof([Slots() for i in range(1000)])
Out[1]: 4516
>>> sys.getsizeof([NoSlots() for i in range(1000)])
Out[1]: 4516

我預計至少 Python 2.7 的大小會有所不同,所以我假設測試有問題。

不,PEP 412不會使__slots__變得多余。


首先,Armin Rigo 是對的,您沒有正確測量它。 您需要測量的是對象的大小、值、 NoSlots __dict__和鍵(僅適用於NoSlots )。

或者你可以按照他的建議去做:

cls = Slots if len(sys.argv) > 1 else NoSlots
def f():
    tracemalloc.start()
    objs = [cls() for _ in range(100000)]
    print(tracemalloc.get_traced_memory())
f()

當我在 OS X 的 64 位 CPython 3.4 上運行它時,我得到8824968Slots25624872NoSlots 所以,看起來NoSlots實例需要 88 個字節,而Slots實例需要 256 個字節。


這怎么可能?

因為__slots__和鍵拆分__dict__之間仍然存在兩個差異。

首先,字典使用的哈希表保持低於 2/3 滿,並且它們呈指數增長並具有最小大小,因此您將有一些額外的空間。 通過查看評論很好的 來源,不難計算出有多少空間:您將擁有 8 個哈希桶而不是 5 個槽指針。

其次,字典本身不是免費的; 它有一個標准的對象頭、一個計數和兩個指針。 這聽起來可能不是很多,但是當你談論一個只有幾個屬性的對象時(請注意,大多數對象只有幾個屬性......),dict 頭可以像哈希表一樣產生很大的不同。

當然,在您的示例中,值,所以這里涉及的唯一成本是對象本身,加上 5 個槽或 8 個哈希桶和 dict 標頭,所以差異非常顯着。 在現實生活中, __slots__很少有那么大的好處。


最后,請注意 PEP 412 僅聲稱:

基准測試顯示面向對象程序的內存使用減少了 10% 到 20%

想想你在哪里使用__slots__ 要么節省的錢太多以至於不使用__slots__會很荒謬,要么你真的需要擠出最后的 15%。 或者,您正在構建一個 ABC 或其他類,您希望這些類被誰知道是什么子類化,而這些子類可能需要節省。 無論如何,在這些情況下,即使沒有__slots__也能獲得一半的收益,甚至三分之二的收益,這仍然遠遠不夠; 你仍然需要使用__slots__

真正的勝利是在不值得使用__slots__的情況下; 您將免費獲得小額福利。

(此外,肯定有一些程序員過度使用了__slots__ ,也許這個變化可以說服他們中的一些人將他們的精力投入到微優化其他不太無關緊要的事情上,如果你幸運的話。)

問題是sys.getsizeof() ,它很少返回您期望的結果。 例如,在這種情況下,它計算對象的“大小”而不考慮其__dict__的大小。 我建議您通過測量創建 100'000 個實例的實際內存使用量來重試。

另請注意,Python 3.3 的行為受到 PyPy 的啟發,其中__slots__沒有區別,因此我希望它在 Python 3.3 中也沒有區別。 據我所知, __slots__現在幾乎沒有任何用處。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM