2010-10-31 118 views
6

說我有一個A類:什麼結構是存儲在內存中的Python對象?

class A(object): 
    def __init__(self, x): 
     self.x = x 

    def __str__(self): 
     return self.x 

我使用sys.getsizeof,瞭解有多少字節實例A需要:

>>> sys.getsizeof(A(1)) 
64 
>>> sys.getsizeof(A('a')) 
64 
>>> sys.getsizeof(A('aaa')) 
64 

如實驗所示的上方,一個A對象的大小無論什麼self.x都是一樣的。

所以我想知道python如何在內部存儲對象?

在一個新的類實例getsizeof的情況下
+0

這在python實現中肯定會有所不同。你在說哪一個? – 2010-10-31 10:38:54

回答

22

這取決於什麼樣的對象,也是其Python實現:-)

在CPython的,這是當他們使用python,所有的Python對象由一個C結構來表示什麼人使用, PyObject。所有'存儲對象'真的存儲PyObject *PyObject結構保存最基本的信息:對象的類型(指向另一個PyObject的指針)及其引用計數(一個ssize_t大小的整數。)C中定義的類型通過將額外信息存儲在對象本身中來擴展此結構,有時會分別分配額外的數據。

例如,元組存儲其長度和PyObject指針它們所包含的結構本身(內(作爲「延伸」一個的PyObject結構一個PyTupleObject實現)的結構體包含在該定義中的1長度的數組,但執行分配一個正確大小的內存塊可容納PyTupleObject結構加上與元組應該容納的項目數量一樣多)。同樣的方式,字符串(PyStringObject)存儲它們的長度,緩存散列值,一些字符串緩存(「interning」)簿記以及他們數據的實際char *。因此元組和字符串是單塊內存。

另一方面,列表(PyListObject)存儲它們的長度,其數據爲PyObject **,另一個爲ssize_t以跟蹤它們爲數據分配多少空間。因爲Python在各處都存儲PyObject指針,所以一旦分配了PyObject結構,就不能生長它 - 這樣做可能需要結構體移動,這意味着要查找所有指針並更新它們。由於列表可能需要增長,因此必須從PyObject結構中分別分配數據。元組和字符串不能增長,所以他們不需要這個。 Dicts(PyDictObject)以相同的方式工作,儘管它們存儲密鑰的密鑰,值和緩存散列值,而不僅僅是項目。字典也有一些額外的開銷,以適應小型字典和專門的查找功能。

但是,這些都是C語言中的所有類型,通過查看C源代碼,您通常可以看到它們將使用多少內存。在Python而不是C中定義的類的實例並不那麼容易。最簡單的情況是經典類的實例並不困難:它是一個PyObject,它將類PyObject *存儲到其類中(與存儲在PyObject結構中的類型不同),PyObject *__dict__屬性(其中保持所有其他實例屬性)和PyObject *其weakreflist(其由weakref模塊使用,並且如果必要的話僅初始化。)的實例的__dict__通常是唯一的實例,因此計算這樣的情況下的「內存大小」時你通常也要計算屬性字典的大小。但它並不一定是特定的實例! __dict__可以被分配到很好。

新風格的禮儀複雜化。與經典類不同,新樣式類的實例不是單獨的C類型,因此它們不需要單獨存儲對象的類。他們也有餘地__dict__和weakreflist參考,但不同於經典的情況下,他們不要求任意屬性__dict__屬性。如果類(及其所有基類)使用__slots__來定義一組嚴格的屬性,並且這些屬性都不是名稱__dict__,則該實例不允許任意屬性且不分配字典。另一方面,由__slots__定義的屬性必須被存儲在的某處。這是通過存儲PyObject指針直接在的PyObject結構的那些屬性的值來實現,就像是與寫入在C.在__slots__每個條目從而會佔用PyObject *,不管類型所做的屬性是否被設置或不。

所有這一切說,這個問題仍然是因爲一切都在Python是保持對象只持有一個參考對象和一切,它有時很難繪製對象之間的界線。兩個對象可以引用相同的數據位。他們可能只有兩個引用該數據。擺脫這兩個對象也擺脫了數據。他們是否都擁有這些數據?只有其中一個,但如果是的話,哪一個?或者你會說他們擁有一半的數據,即使擺脫一個對象不會釋放一半的數據? Weakrefs可以使這個更加複雜:兩個對象可以參考相同的數據,但刪除的對象之一可能會導致其他物體擺脫其引用到的數據,使數據終究清理。

幸運的是共同情況下是相當容易弄清楚。有Python的內存調試器,可以在跟蹤這些事情時做出合理的工作,如heapy。只要你的類(及其基類)相當簡單,你就可以猜測它將佔用多少內存 - 特別是大量內存。如果您確實想知道數據結構的確切大小,請查閱CPython源代碼;大多數內建類型都是在Include/<type>object.h中描述並在Objects/<type>object.c中實現的簡單結構。 PyObject結構本身在Include/object.h中描述。請記住:它是一路向下的指針;那些也佔據了房間。

+0

非常感謝。實際上,我在問這個問題,因爲我想知道當我調用'cache.set(key,obj)'時,存儲在memcached中的內容是什麼,它是否像一個醃製的對象? – satoru 2010-10-31 12:43:28

+4

哦,好!這是一個完全不同的問題。據我所知(並快速瀏覽源代碼確認),memcache模塊存儲對象的pickle版本,是的。它還爲每個商店創建了一個新的Pickler,因此存儲兩個引用同一個第三個對象的對象意味着第三個對象會被醃兩次(除非您的對象不會以這種方式醃製,當然,您可以定義酸洗的確切方式)換句話說,你的問題的答案是'len(pickle.dumps(obj))'。 – 2010-10-31 16:00:28

+0

對於圖形好奇,我曾經測試並繪製了多種內置類型:http://stackoverflow.com/a/30008338/2087463 – tmthydvnprt 2016-03-13 13:31:27

相關問題