python使用函数可以减少内存吗_如何将Python内存占用缩小20倍?
當程序執(zhí)行過程中RAM中有大量對象處于活動狀態(tài)時,可能會出現(xiàn)內(nèi)存問題,特別是在對可用內(nèi)存總量有限制的情況下。
下面概述了一些減小對象大小的方法,這些方法可以顯著減少純Python程序所需的RAM數(shù)量。
注: 這是我原帖子的英文版本(原帖子是用俄文寫的)。(https://habr.com/ru/post/455722/ )
為了簡單起見,我們將考慮用Python中的結構來表示坐標為x、y、z的點,并通過名稱來訪問坐標值。
Dict
在小程序中,特別是在腳本中,使用內(nèi)置的dict來表示結構信息是非常簡單方便的:
隨著Python 3.6中使用一組有序鍵的更緊湊實現(xiàn)方式的出現(xiàn),dict變得更有吸引力。但是,讓我們看看它在RAM中的內(nèi)存大小:
它需要大量內(nèi)存,特別是當你突然需要創(chuàng)建大量實例時:
類實例
對于那些喜歡將所有東西放置在類中的人來說,最好將結構定義為一個可以通過屬性名訪問的類,:
類實例的結構很有趣:
這里的__weakref__是對這個對象的所謂弱引用列表的一個引用,__dict__字段是對類實例字典的引用,它包含實例屬性的值(注意64位的引用平臺會占用8個字節(jié))。從Python 3.3開始,共享空間用于在字典中存儲類的所有實例的鍵。這減少了RAM中實例堆棧的大小:
因此,大量的類實例占用的內(nèi)存比一個普通字典(dict)占用的要小:
很容易看出,由于實例字典的大小,RAM中實例的大小仍然很大。
帶有__slots__的類實例
通過消除 __dict__和__weakref__,可以顯著減小RAM中的類實例的大小。這通過一個帶有__slots__的小“技巧”是可能實現(xiàn)的:
RAM中的對象大小明顯變小了:
在類定義中使用__slots__可以顯著減少大量實例對內(nèi)存空間的占用:
目前,這是大幅度減少RAM中類實例的內(nèi)存占用的主要方法。
這是因為在內(nèi)存中,對象引用會緊跟標題之后被存儲在內(nèi)存中——屬性值,并通過類字典中的特殊描述符來訪問它們:
要自動化使用 __slots__創(chuàng)建一個類的過程,有一個庫[namedlist] (https://pypi.org/project/namedlist )可以使用。namedlist.namedlist函數(shù)會創(chuàng)建一個帶有__slots__的類:
另一個包[attrs] (https://pypi.org/project/attrs )允許你使用和不使用__slots__自動創(chuàng)建類。
元組
Python還有一個內(nèi)置的類型tuple(元組),用于表示不可變的數(shù)據(jù)結構。一個元組是一個固定的結構或記錄,但沒有字段名。對于字段訪問,使用的是字段索引。元組字段在元組實例創(chuàng)建時就一次性與值對象相關聯(lián):
元組的實例是相當簡潔的:
它們在內(nèi)存中占用的字節(jié)比使用__slots__的類實例要多8個字節(jié),因為內(nèi)存中的元組跟蹤也包含許多字段:
Namedtuple(命名元組)
由于元組使用的非常廣泛,某天有人可能會提交一個通過名稱訪問字段的請求。這個請求的答案是collections.namedtuple模塊。
namedtuple函數(shù)的目的是自動生成這樣的類:
它會創(chuàng)建一個元組子類,其中定義了用于按名稱訪問字段的描述符。在我們的例子中,它看起來是這樣的:
這些類的所有實例都具有與元組相同的內(nèi)存占用量。大量的實例會占用更大的內(nèi)存空間:
Recordclass: 沒有循環(huán)GC的可變namedtuple
由于tuple和相應的namedtuple類會生成不可變對象,因此,ob.x屬性就不能再與另一個值對象相關聯(lián)了,對可變namedtuple變體的請求已經(jīng)出現(xiàn)了。由于Python中沒有與支持賦值的元組相同的內(nèi)置類型,因此,開發(fā)者們創(chuàng)建了許多選項。我們將關注[recordclass] (https://pypi.org/project/recordclass ),它的評級為[stackoverflow] (https://stackoverflow.com/questions/29290359/ exists -of-mutable-name - tuplein -python / 29419745 )。此外,與類元組對象的大小相比,它還可以用來減小RAM中對象的大小。
包recordclass引入了recordclass.mutabletuple類型,它幾乎與tuple相同,但它支持賦值。在此基礎上,創(chuàng)建的子類幾乎與namedtuple完全相同,但它支持將新值賦給字段(不需要創(chuàng)建新的實例)。recordclass函數(shù)與namedtuple函數(shù)一樣,允許你自動創(chuàng)建這些類:
只有在沒有PyGC_Head的情況下,類實例才具有與tuple相同的結構:
默認情況下,recordclass函數(shù)會創(chuàng)建一個不參與循環(huán)垃圾回收機制的類。通常,namedtuple和recordclass用于生成表示記錄或簡單(非遞歸)數(shù)據(jù)結構的類。在Python中正確使用它們就不會生成循環(huán)引用。出于這個原因, 在recordclass生成的類實例后面 ,默認情況下,PyGC_Head 部分會被排除在外, 這對支持循環(huán)垃圾回收機制(更準確地說:在與創(chuàng)建的類相關聯(lián)的PyTypeObject結構中,默認情況下,flag字段中的Py_TPFLAGS_HAVE_GC是沒有設置的)的類來說是必要的。
大量實例的內(nèi)存占用量比使用了__slots__的類的實例要小:
Dataobject
recordclass庫中提出的另一個解決方案是基于這樣的思想:在內(nèi)存中使用與帶有__slots__的類實例相同的存儲結構,但不參與循環(huán)垃圾回收機制。這些類是使用recordclass.make_dataclass數(shù)生成的:
默認情況下,以這種方式創(chuàng)建的類將創(chuàng)建可變實例。
另一種方法——使用繼承自recordclass.dataobject的類聲明:
以這種方式創(chuàng)建的類將創(chuàng)建不參與循環(huán)垃圾回收機制的實例。內(nèi)存中實例的結構與使用__slots__的情況相同,但是沒有PyGC_Head:
為了訪問字段,還可以使用特殊的描述符通過它從對象開始的偏移量來訪問,這些偏移量位于類字典中:
大量實例內(nèi)存占用量的大小在CPython中可能是最小的:
Cython
還有一種基于使用[Cython] (https://cython.org )的方法。它的優(yōu)點是字段可以接受C語言原子類型的值。自動創(chuàng)建用于從純Python中來訪問字段的描述符。例如:
在這種情況下,實例的內(nèi)存占用更小:
內(nèi)存中的實例跟蹤的結構如下:
大量副本的占用空間要小一些:
但是,請記住,當你從Python代碼訪問時,每次都會執(zhí)行從int到Python對象的轉(zhuǎn)換,反之亦然。
Numpy
對大量數(shù)據(jù)使用多維數(shù)組或記錄數(shù)組會增加內(nèi)存占用。但是,為了在純Python中進行有效的處理,你應該使用那些主要使用了numpy包中的函數(shù)的處理方法。
使用函數(shù)創(chuàng)建一個由N個元素組成的數(shù)組,并將其初始化為0:
內(nèi)存中數(shù)組的大小是可能的最小值:
正常訪問數(shù)組元素和行需要將Python對象轉(zhuǎn)換為C中的 int值,反之亦然。提取單個行會創(chuàng)建一個包含單個元素的數(shù)組。它的追蹤就不再那么簡單了:
因此,如上所述,在Python代碼中,有必要使用numpy包中的函數(shù)來處理數(shù)組。
結論
通過一個清晰而簡單的示例,可以驗證由開發(fā)人員和用戶組成的Python編程語言(CPython)社區(qū)確實有可能顯著減少對象使用的內(nèi)存量。
英文原文:https://habr.com/en/post/458518/
譯者:Nothing
總結
以上是生活随笔為你收集整理的python使用函数可以减少内存吗_如何将Python内存占用缩小20倍?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我们注意到您的计算机目前处于离线状态_如
- 下一篇: 递归删除单链表中所有值为x的元素_如何纯