亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何用Python代碼減少Python所需的內存

發布時間:2021-10-26 10:55:12 來源:億速云 閱讀:468 作者:柒染 欄目:編程語言

如何用Python代碼減少Python所需的內存,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

在執行程序時,如果內存中有大量活動的對象,就可能出現內存問題,尤其是在可用內存總量有限的情況下。在本文中,我們將討論縮小對象的方法,大幅減少 Python 所需的內存。

如何用Python代碼減少Python所需的內存

為了簡便起見,我們以一個表示點的 Python 結構為例,它包括 x、y、z 坐標值,坐標值可以通過名稱訪問。

Dict

在小型程序中,特別是在腳本中,使用 Python 自帶的 dict 來表示結構信息非常簡單方便:

>>> ob = {'x':1, 'y':2, 'z':3}
>>> x = ob['x']
>>> ob['y'] = y

由于在 Python 3.6 中 dict 的實現采用了一組有序鍵,因此其結構更為緊湊,更深得人心。但是,讓我們看看 dict 在內容中占用的空間大小:

>>> print(sys.getsizeof(ob))
240

如上所示,dict 占用了大量內存,尤其是如果突然虛需要創建大量實例時:

如何用Python代碼減少Python所需的內存

類實例

有些人希望將所有東西都封裝到類中,他們更喜歡將結構定義為可以通過屬性名訪問的類:

class Point:
 #
 def __init__(self, x, y, z):
 self.x = x
 self.y = y
 self.z = z
>>> ob = Point(1,2,3)
>>> x = ob.x
>>> ob.y = y

類實例的結構很有趣:

如何用Python代碼減少Python所需的內存

在上表中,__weakref__ 是該列表的引用,稱之為到該對象的弱引用(weak reference);字段 __dict__ 是該類的實例字典的引用,其中包含實例屬性的值(注意在 64-bit 引用平臺中占用 8 字節)。從 Python 3.3 開始,所有類實例的字典的鍵都存儲在共享空間中。這樣就減少了內存中實例的大小:

>>> print(sys.getsizeof(ob), sys.getsizeof(ob.__dict__)) 
56 112

因此,大量類實例在內存中占用的空間少于常規字典(dict):

如何用Python代碼減少Python所需的內存

不難看出,由于實例的字典很大,所以實例依然占用了大量內存。

帶有 __slots__ 的類實例

為了大幅降低內存中類實例的大小,我們可以考慮干掉 __dict__ 和__weakref__。為此,我們可以借助 __slots__:

class Point:
 __slots__ = 'x', 'y', 'z'
 def __init__(self, x, y, z):
 self.x = x
 self.y = y
 self.z = z
>>> ob = Point(1,2,3)
>>> print(sys.getsizeof(ob))
64

如此一來,內存中的對象就明顯變小了:

如何用Python代碼減少Python所需的內存

在類的定義中使用了 __slots__ 以后,大量實例占據的內存就明顯減少了:

實例數

如何用Python代碼減少Python所需的內存

目前,這是降低類實例占用內存的主要方式。

這種方式減少內存的原理為:在內存中,對象的標題后面存儲的是對象的引用(即屬性值),訪問這些屬性值可以使用類字典中的特殊描述符:

>>> pprint(Point.__dict__)
mappingproxy(
 ....................................
 'x': <member 'x' of 'Point' objects>,
 'y': <member 'y' of 'Point' objects>,
 'z': <member 'z' of 'Point' objects>})

為了自動化使用 __slots__ 創建類的過程,你可以使用庫namedlist(https://pypi.org/project/namedlist)。namedlist.namedlist 函數可以創建帶有 __slots__ 的類:

>>> Point = namedlist('Point', ('x', 'y', 'z'))

還有一個包 attrs(https://pypi.org/project/attrs),無論使用或不使用 __slots__ 都可以利用這個包自動創建類。

元組

Python 還有一個自帶的元組(tuple)類型,代表不可修改的數據結構。元組是固定的結構或記錄,但它不包含字段名稱。你可以利用字段索引訪問元組的字段。在創建元組實例時,元組的字段會一次性關聯到值對象:

>>> ob = (1,2,3)
>>> x = ob[0]
>>> ob[1] = y # ERROR

元組實例非常緊湊:

>>> print(sys.getsizeof(ob))
72

由于內存中的元組還包含字段數,因此需要占據內存的 8 個字節,多于帶有 __slots__ 的類:

如何用Python代碼減少Python所需的內存

命名元組

由于元組的使用非常廣泛,所以終有一天你需要通過名稱訪問元組。為了滿足這種需求,你可以使用模塊 collections.namedtuple。

namedtuple 函數可以自動生成這種類:

>>> Point = namedtuple('Point', ('x', 'y', 'z'))

如上代碼創建了元組的子類,其中還定義了通過名稱訪問字段的描述符。對于上述示例,訪問方式如下:

 class Point(tuple):
 #
 @property
 def _get_x(self):
 return self[0]
 @property
 def _get_y(self):
 return self[1]
 @property
 def _get_z(self):
 return self[2]
 #
 def __new__(cls, x, y, z):
 return tuple.__new__(cls, (x, y, z))

這種類所有的實例所占用的內存與元組完全相同。但大量的實例占用的內存也會稍稍多一些:

如何用Python代碼減少Python所需的內存

記錄類:不帶循環 GC 的可變更命名元組

由于元組及其相應的命名元組類能夠生成不可修改的對象,因此類似于 ob.x 的對象值不能再被賦予其他值,所以有時還需要可修改的命名元組。由于 Python 沒有相當于元組且支持賦值的內置類型,因此人們想了許多辦法。在這里我們討論一下記錄類(recordclass,https://pypi.org/project/recordclass),它在 StackoverFlow 上廣受好評(https://stackoverflow.com/questions/29290359/existence-of-mutable-named-tuple-in)。

此外,它還可以將對象占用的內存量減少到與元組對象差不多的水平。

recordclass 包引入了類型 recordclass.mutabletuple,它幾乎等價于元組,但它支持賦值。它會創建幾乎與 namedtuple 完全一致的子類,但支持給屬性賦新值(而不需要創建新的實例)。recordclass 函數與 namedtuple 函數類似,可以自動創建這些類:

 >>> Point = recordclass('Point', ('x', 'y', 'z'))
 >>> ob = Point(1, 2, 3)

類實例的結構也類似于 tuple,但沒有 PyGC_Head:

如何用Python代碼減少Python所需的內存

在默認情況下,recordclass 函數會創建一個類,該類不參與垃圾回收機制。一般來說,namedtuple 和 recordclass 都可以生成表示記錄或簡單數據結構(即非遞歸結構)的類。在 Python 中正確使用這二者不會造成循環引用。因此,recordclass 生成的類實例默認情況下不包含 PyGC_Head 片段(這個片段是支持循環垃圾回收機制的必需字段,或者更準確地說,在創建類的 PyTypeObject 結構中,flags 字段默認情況下不會設置 Py_TPFLAGS_HAVE_GC 標志)。

大量實例占用的內存量要小于帶有 __slots__ 的類實例:

如何用Python代碼減少Python所需的內存

dataobject

recordclass 庫提出的另一個解決方案的基本想法為:內存結構采用與帶 __slots__ 的類實例同樣的結構,但不參與循環垃圾回收機制。這種類可以通過 recordclass.make_dataclass 函數生成:

>>> Point = make_dataclass('Point', ('x', 'y', 'z'))

這種方式創建的類默認會生成可修改的實例。

另一種方法是從 recordclass.dataobject 繼承:

class Point(dataobject):
 x:int
 y:int
 z:int

這種方法創建的類實例不會參與循環垃圾回收機制。內存中實例的結構與帶有 __slots__ 的類相同,但沒有 PyGC_Head:

如何用Python代碼減少Python所需的內存

>>> ob = Point(1,2,3)
>>> print(sys.getsizeof(ob))
40

如果想訪問字段,則需要使用特殊的描述符來表示從對象開頭算起的偏移量,其位置位于類字典內:

mappingproxy({'__new__': <staticmethod at 0x7f203c4e6be0>,
 .......................................
 'x': <recordclass.dataobject.dataslotgetset at 0x7f203c55c690>,
 'y': <recordclass.dataobject.dataslotgetset at 0x7f203c55c670>,
 'z': <recordclass.dataobject.dataslotgetset at 0x7f203c55c410>})

大量實例占用的內存量在 CPython 實現中是最小的:

如何用Python代碼減少Python所需的內存

Cython

還有一個基于 Cython(https://cython.org/)的方案。該方案的優點是字段可以使用 C 語言的原子類型。訪問字段的描述符可以通過純 Python 創建。例如:

cdef class Python:
 cdef public int x, y, z
 def __init__(self, x, y, z):
 self.x = x
 self.y = y
 self.z = z

本例中實例占用的內存更小:

>>> ob = Point(1,2,3)
>>> print(sys.getsizeof(ob))
32

內存結構如下:

如何用Python代碼減少Python所需的內存

大量副本所占用的內存量也很小:

如何用Python代碼減少Python所需的內存

但是,需要記住在從 Python 代碼訪問時,每次訪問都會引發 int 類型和 Python 對象之間的轉換。

Numpy

使用擁有大量數據的多維數組或記錄數組會占用大量內存。但是,為了有效地利用純 Python 處理數據,你應該使用 Numpy 包提供的函數。

>>> Point = numpy.dtype(('x', numpy.int32), ('y', numpy.int32), ('z', numpy.int32)])

一個擁有 N 個元素、初始化成零的數組可以通過下面的函數創建:

 >>> points = numpy.zeros(N, dtype=Point)

內存占用是最小的:

如何用Python代碼減少Python所需的內存

一般情況下,訪問數組元素和行會引發 Python 對象與 C 語言 int 值之間的轉換。如果從生成的數組中獲取一行結果,其中包含一個元素,其內存就沒那么緊湊了:

 >>> sys.getsizeof(points[0])
 68

因此,如上所述,在 Python 代碼中需要使用 numpy 包提供的函數來處理數組。

看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

武陟县| 伊川县| 昌宁县| 英德市| 济源市| 昭觉县| 当涂县| 洞头县| 休宁县| 奉化市| 乌拉特前旗| 高邮市| 庆城县| 荆州市| 红桥区| 湘潭市| 军事| 南康市| 漾濞| 都兰县| 杭锦后旗| 宜良县| 和硕县| 永定县| 威远县| 黄浦区| 古丈县| 屏东县| 永吉县| 济阳县| 丹东市| 贵溪市| 抚州市| 平原县| 肥城市| 靖江市| 军事| 周口市| 房产| 涪陵区| 固镇县|