python减小内存占用_如何将Python内存占用缩小20倍?
當程序執行過程中RAM中有大量對象處于活動狀態時,可能會出現內存問題,特別是在對可用內存總量有限制的情況下。
下面概述了一些減小對象大小的方法,這些方法可以顯著減少純Python程序所需的RAM數量。
注: 這是我原帖子的英文版本(原帖子是用俄文寫的)。(https://habr.com/ru/post/455722/ )
為了簡單起見,我們將考慮用Python中的結構來表示坐標為x、y、z的點,并通過名稱來訪問坐標值。
Dict
在小程序中,特別是在腳本中,使用內置的dict來表示結構信息是非常簡單方便的:
如何將Python內存占用縮小20倍?-1.jpg (6.06 KB, 下載次數: 0)
2020-11-8 08:06 上傳
隨著Python 3.6中使用一組有序鍵的更緊湊實現方式的出現,dict變得更有吸引力。但是,讓我們看看它在RAM中的內存大小:
如何將Python內存占用縮小20倍?-2.jpg (4.43 KB, 下載次數: 0)
2020-11-8 08:06 上傳
它需要大量內存,特別是當你突然需要創建大量實例時:
如何將Python內存占用縮小20倍?-3.jpg (17.2 KB, 下載次數: 0)
2020-11-8 08:06 上傳
類實例
對于那些喜歡將所有東西放置在類中的人來說,最好將結構定義為一個可以通過屬性名訪問的類,:
如何將Python內存占用縮小20倍?-4.jpg (10.78 KB, 下載次數: 1)
2020-11-8 08:06 上傳
類實例的結構很有趣:
如何將Python內存占用縮小20倍?-5.jpg (18.95 KB, 下載次數: 0)
2020-11-8 08:06 上傳
這里的__weakref__是對這個對象的所謂弱引用列表的一個引用,__dict__字段是對類實例字典的引用,它包含實例屬性的值(注意64位的引用平臺會占用8個字節)。從Python 3.3開始,共享空間用于在字典中存儲類的所有實例的鍵。這減少了RAM中實例堆棧的大小:
如何將Python內存占用縮小20倍?-6.jpg (6.8 KB, 下載次數: 0)
2020-11-8 08:06 上傳
因此,大量的類實例占用的內存比一個普通字典(dict)占用的要小:
如何將Python內存占用縮小20倍?-7.jpg (16.23 KB, 下載次數: 0)
2020-11-8 08:06 上傳
很容易看出,由于實例字典的大小,RAM中實例的大小依舊很大。
帶有__slots__的類實例
通過消除 __dict__和__weakref__,可以顯著減小RAM中的類實例的大小。這通過一個帶有__slots__的小“技巧”是可能實現的:
如何將Python內存占用縮小20倍?-8.jpg (12.89 KB, 下載次數: 0)
2020-11-8 08:06 上傳
RAM中的對象大小明顯變小了:
如何將Python內存占用縮小20倍?-9.jpg (18.05 KB, 下載次數: 0)
2020-11-8 08:06 上傳
在類定義中使用__slots__可以顯著減少大量實例對內存空間的占用:
如何將Python內存占用縮小20倍?-10.jpg (16.62 KB, 下載次數: 0)
2020-11-8 08:06 上傳
目前,這是大幅度減少RAM中類實例的內存占用的主要方法。
這是因為在內存中,對象引用會緊跟標題之后被存儲在內存中——屬性值,并通過類字典中的特殊描述符來訪問它們:
如何將Python內存占用縮小20倍?-11.jpg (15 KB, 下載次數: 0)
2020-11-8 08:06 上傳
要自動化使用 __slots__創建一個類的過程,有一個庫[namedlist] (https://pypi.org/project/namedlist )可以使用。namedlist.namedlist函數會創建一個帶有__slots__的類:
如何將Python內存占用縮小20倍?-12.jpg (4.88 KB, 下載次數: 1)
2020-11-8 08:06 上傳
另一個包[attrs] (https://pypi.org/project/attrs )允許你使用和不使用__slots__自動創建類。
元組
Python還有一個內置的類型tuple(元組),用于表示不可變的數據結構。一個元組是一個固定的結構或記錄,但沒有字段名。對于字段訪問,使用的是字段索引。元組字段在元組實例創建時就一次性與值對象相關聯:
如何將Python內存占用縮小20倍?-13.jpg (5.77 KB, 下載次數: 0)
2020-11-8 08:06 上傳
元組的實例是相當簡潔的:
如何將Python內存占用縮小20倍?-14.jpg (4.37 KB, 下載次數: 0)
2020-11-8 08:06 上傳
它們在內存中占用的字節比使用__slots__的類實例要多8個字節,因為內存中的元組跟蹤也包含許多字段:
如何將Python內存占用縮小20倍?-15.jpg (20.47 KB, 下載次數: 1)
2020-11-8 08:06 上傳
Namedtuple(命名元組)
由于元組使用的非常廣泛,某天有人可能會提交一個通過名稱訪問字段的請求。這個請求的答案是collections.namedtuple模塊。
namedtuple函數的目的是自動生成這樣的類:
如何將Python內存占用縮小20倍?-16.jpg (4.96 KB, 下載次數: 0)
2020-11-8 08:06 上傳
它會創建一個元組子類,其中定義了用于按名稱訪問字段的描述符。在我們的例子中,它看起來是這樣的:
如何將Python內存占用縮小20倍?-17.jpg (18.53 KB, 下載次數: 0)
2020-11-8 08:06 上傳
這些類的所有實例都具有與元組相同的內存占用量。大量的實例會占用更大的內存空間:
如何將Python內存占用縮小20倍?-18.jpg (15.94 KB, 下載次數: 0)
2020-11-8 08:06 上傳
Recordclass: 沒有循環GC的可變namedtuple
由于tuple和相應的namedtuple類會生成不可變對象,因此,ob.x屬性就不能再與另一個值對象相關聯了,對可變namedtuple變體的請求已經出現了。由于Python中沒有與支持賦值的元組相同的內置類型,因此,開發者們創建了許多選項。我們將關注[recordclass] (https://pypi.org/project/recordclass ),它的評級為[stackoverflow] (https://stackoverflow.com/questions/29290359/ exists -of-mutable-name - tuplein -python / 29419745 )。此外,與類元組對象的大小相比,它還可以用來減小RAM中對象的大小。
包recordclass引入了recordclass.mutabletuple類型,它幾乎與tuple相同,但它支持賦值。在此基礎上,創建的子類幾乎與namedtuple完全相同,但它支持將新值賦給字段(不需要創建新的實例)。recordclass函數與namedtuple函數一樣,允許你自動創建這些類:
如何將Python內存占用縮小20倍?-19.jpg (6.88 KB, 下載次數: 0)
2020-11-8 08:06 上傳
只有在沒有PyGC_Head的情況下,類實例才具有與tuple相同的結構:
如何將Python內存占用縮小20倍?-20.jpg (16.68 KB, 下載次數: 2)
2020-11-8 08:06 上傳
默認情況下,recordclass函數會創建一個不參與循環垃圾回收機制的類。通常,namedtuple和recordclass用于生成表示記錄或簡單(非遞歸)數據結構的類。在Python中正確使用它們就不會生成循環引用。出于這個原因, 在recordclass生成的類實例后面 ,默認情況下,PyGC_Head 部分會被排除在外, 這對支持循環垃圾回收機制(更準確地說:在與創建的類相關聯的PyTypeObject結構中,默認情況下,flag字段中的Py_TPFLAGS_HAVE_GC是沒有設置的)的類來說是必要的。
大量實例的內存占用量比使用了__slots__的類的實例要小:
如何將Python內存占用縮小20倍?-21.jpg (17.23 KB, 下載次數: 1)
2020-11-8 08:06 上傳
Dataobject
recordclass庫中提出的另一個解決方案是基于這樣的思想:在內存中使用與帶有__slots__的類實例相同的存儲結構,但不參與循環垃圾回收機制。這些類是使用recordclass.make_dataclass數生成的:
如何將Python內存占用縮小20倍?-22.jpg (5.37 KB, 下載次數: 0)
2020-11-8 08:06 上傳
默認情況下,以這種方式創建的類將創建可變實例。
另一種方法——使用繼承自recordclass.dataobject的類聲明:
如何將Python內存占用縮小20倍?-23.jpg (5.7 KB, 下載次數: 0)
2020-11-8 08:06 上傳
以這種方式創建的類將創建不參與循環垃圾回收機制的實例。內存中實例的結構與使用__slots__的情況相同,但是沒有PyGC_Head:
如何將Python內存占用縮小20倍?-24.jpg (15.58 KB, 下載次數: 0)
2020-11-8 08:06 上傳
如何將Python內存占用縮小20倍?-25.jpg (6.32 KB, 下載次數: 0)
2020-11-8 08:06 上傳
為了訪問字段,還可以使用特殊的描述符通過它從對象開始的偏移量來訪問,這些偏移量位于類字典中:
如何將Python內存占用縮小20倍?-26.jpg (23.75 KB, 下載次數: 0)
2020-11-8 08:06 上傳
大量實例內存占用量的大小在CPython中可能是最小的:
如何將Python內存占用縮小20倍?-27.jpg (17.04 KB, 下載次數: 1)
2020-11-8 08:06 上傳
Cython
還有一種基于使用[Cython] (https://cython.org )的方法。它的優點是字段可以接受C語言原子類型的值。自動創建用于從純Python中來訪問字段的描述符。例如:
如何將Python內存占用縮小20倍?-28.jpg (10.66 KB, 下載次數: 0)
2020-11-8 08:06 上傳
在這種情況下,實例的內存占用更小:
如何將Python內存占用縮小20倍?-29.jpg (6.37 KB, 下載次數: 0)
2020-11-8 08:06 上傳
內存中的實例跟蹤的結構如下:
如何將Python內存占用縮小20倍?-30.jpg (18.13 KB, 下載次數: 0)
2020-11-8 08:06 上傳
大量副本的占用空間要小一些:
如何將Python內存占用縮小20倍?-31.jpg (15.98 KB, 下載次數: 0)
2020-11-8 08:06 上傳
但是,請記住,當你從Python代碼訪問時,每次都會執行從int到Python對象的轉換,反之亦然。
Numpy
對大量數據使用多維數組或記錄數組會增加內存占用。但是,為了在純Python中進行有效的處理,你應該使用那些主要使用了numpy包中的函數的處理方法。
如何將Python內存占用縮小20倍?-32.jpg (7.62 KB, 下載次數: 0)
2020-11-8 08:06 上傳
使用函數創建一個由N個元素組成的數組,并將其初始化為0:
如何將Python內存占用縮小20倍?-33.jpg (4.91 KB, 下載次數: 0)
2020-11-8 08:06 上傳
內存中數組的大小是可能的最小值:
如何將Python內存占用縮小20倍?-34.jpg (16.92 KB, 下載次數: 1)
2020-11-8 08:06 上傳
正常訪問數組元素和行需要將Python對象轉換為C中的 int值,反之亦然。提取單個行會創建一個包含單個元素的數組。它的追蹤就不再那么簡單了:
如何將Python內存占用縮小20倍?-35.jpg (4.51 KB, 下載次數: 1)
2020-11-8 08:06 上傳
因此,如上所述,在Python代碼中,有必要使用numpy包中的函數來處理數組。
結論
通過一個清晰而簡單的示例,可以驗證由開發人員和用戶組成的Python編程語言(CPython)社區確實有可能顯著減少對象使用的內存量。
英文原文:https://habr.com/en/post/458518/
譯者:Nothing
總結
以上是生活随笔為你收集整理的python减小内存占用_如何将Python内存占用缩小20倍?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql64如何配置_win7 64位
- 下一篇: 2022-2028年中国城市地下综合管廊