真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

如何用Python代碼減少Python所需的內(nèi)存-創(chuàng)新互聯(lián)

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

成都創(chuàng)新互聯(lián)是一家專注于成都網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì)與策劃設(shè)計(jì),歷城網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)10多年,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:歷城等地區(qū)。歷城做網(wǎng)站價(jià)格咨詢:028-86922220

在執(zhí)行程序時(shí),如果內(nèi)存中有大量活動(dòng)的對(duì)象,就可能出現(xiàn)內(nèi)存問題,尤其是在可用內(nèi)存總量有限的情況下。在本文中,我們將討論縮小對(duì)象的方法,大幅減少 Python 所需的內(nèi)存。

如何用Python代碼減少Python所需的內(nèi)存

為了簡(jiǎn)便起見,我們以一個(gè)表示點(diǎn)的 Python 結(jié)構(gòu)為例,它包括 x、y、z 坐標(biāo)值,坐標(biāo)值可以通過名稱訪問。

Dict

在小型程序中,特別是在腳本中,使用 Python 自帶的 dict 來表示結(jié)構(gòu)信息非常簡(jiǎn)單方便:

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

由于在 Python 3.6 中 dict 的實(shí)現(xiàn)采用了一組有序鍵,因此其結(jié)構(gòu)更為緊湊,更深得人心。但是,讓我們看看 dict 在內(nèi)容中占用的空間大?。?/p>

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

如上所示,dict 占用了大量?jī)?nèi)存,尤其是如果突然虛需要?jiǎng)?chuàng)建大量實(shí)例時(shí):

如何用Python代碼減少Python所需的內(nèi)存

類實(shí)例

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

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

類實(shí)例的結(jié)構(gòu)很有趣:

如何用Python代碼減少Python所需的內(nèi)存

在上表中,__weakref__ 是該列表的引用,稱之為到該對(duì)象的弱引用(weak reference);字段 __dict__ 是該類的實(shí)例字典的引用,其中包含實(shí)例屬性的值(注意在 64-bit 引用平臺(tái)中占用 8 字節(jié))。從 Python 3.3 開始,所有類實(shí)例的字典的鍵都存儲(chǔ)在共享空間中。這樣就減少了內(nèi)存中實(shí)例的大?。?/p>

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

因此,大量類實(shí)例在內(nèi)存中占用的空間少于常規(guī)字典(dict):

如何用Python代碼減少Python所需的內(nèi)存

不難看出,由于實(shí)例的字典很大,所以實(shí)例依然占用了大量?jī)?nèi)存。

帶有 __slots__ 的類實(shí)例

為了大幅降低內(nèi)存中類實(shí)例的大小,我們可以考慮干掉 __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

如此一來,內(nèi)存中的對(duì)象就明顯變小了:

如何用Python代碼減少Python所需的內(nèi)存

在類的定義中使用了 __slots__ 以后,大量實(shí)例占據(jù)的內(nèi)存就明顯減少了:

實(shí)例數(shù)

如何用Python代碼減少Python所需的內(nèi)存

目前,這是降低類實(shí)例占用內(nèi)存的主要方式。

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

>>> pprint(Point.__dict__)
mappingproxy(
 ....................................
 'x': ,
 'y': ,
 'z': })

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

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

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

元組

Python 還有一個(gè)自帶的元組(tuple)類型,代表不可修改的數(shù)據(jù)結(jié)構(gòu)。元組是固定的結(jié)構(gòu)或記錄,但它不包含字段名稱。你可以利用字段索引訪問元組的字段。在創(chuàng)建元組實(shí)例時(shí),元組的字段會(huì)一次性關(guān)聯(lián)到值對(duì)象:

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

元組實(shí)例非常緊湊:

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

由于內(nèi)存中的元組還包含字段數(shù),因此需要占據(jù)內(nèi)存的 8 個(gè)字節(jié),多于帶有 __slots__ 的類:

如何用Python代碼減少Python所需的內(nèi)存

命名元組

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

namedtuple 函數(shù)可以自動(dòng)生成這種類:

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

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

 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))

這種類所有的實(shí)例所占用的內(nèi)存與元組完全相同。但大量的實(shí)例占用的內(nèi)存也會(huì)稍稍多一些:

如何用Python代碼減少Python所需的內(nèi)存

記錄類:不帶循環(huán) GC 的可變更命名元組

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

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

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

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

類實(shí)例的結(jié)構(gòu)也類似于 tuple,但沒有 PyGC_Head:

如何用Python代碼減少Python所需的內(nèi)存

在默認(rèn)情況下,recordclass 函數(shù)會(huì)創(chuàng)建一個(gè)類,該類不參與垃圾回收機(jī)制。一般來說,namedtuple 和 recordclass 都可以生成表示記錄或簡(jiǎn)單數(shù)據(jù)結(jié)構(gòu)(即非遞歸結(jié)構(gòu))的類。在 Python 中正確使用這二者不會(huì)造成循環(huán)引用。因此,recordclass 生成的類實(shí)例默認(rèn)情況下不包含 PyGC_Head 片段(這個(gè)片段是支持循環(huán)垃圾回收機(jī)制的必需字段,或者更準(zhǔn)確地說,在創(chuàng)建類的 PyTypeObject 結(jié)構(gòu)中,flags 字段默認(rèn)情況下不會(huì)設(shè)置 Py_TPFLAGS_HAVE_GC 標(biāo)志)。

大量實(shí)例占用的內(nèi)存量要小于帶有 __slots__ 的類實(shí)例:

如何用Python代碼減少Python所需的內(nèi)存

dataobject

recordclass 庫(kù)提出的另一個(gè)解決方案的基本想法為:內(nèi)存結(jié)構(gòu)采用與帶 __slots__ 的類實(shí)例同樣的結(jié)構(gòu),但不參與循環(huán)垃圾回收機(jī)制。這種類可以通過 recordclass.make_dataclass 函數(shù)生成:

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

這種方式創(chuàng)建的類默認(rèn)會(huì)生成可修改的實(shí)例。

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

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

這種方法創(chuàng)建的類實(shí)例不會(huì)參與循環(huán)垃圾回收機(jī)制。內(nèi)存中實(shí)例的結(jié)構(gòu)與帶有 __slots__ 的類相同,但沒有 PyGC_Head:

如何用Python代碼減少Python所需的內(nèi)存

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

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

mappingproxy({'__new__': ,
 .......................................
 'x': ,
 'y': ,
 'z': })

大量實(shí)例占用的內(nèi)存量在 CPython 實(shí)現(xiàn)中是最小的:

如何用Python代碼減少Python所需的內(nèi)存

Cython

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

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

本例中實(shí)例占用的內(nèi)存更?。?/p>

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

內(nèi)存結(jié)構(gòu)如下:

如何用Python代碼減少Python所需的內(nèi)存

大量副本所占用的內(nèi)存量也很?。?/p>

如何用Python代碼減少Python所需的內(nèi)存

但是,需要記住在從 Python 代碼訪問時(shí),每次訪問都會(huì)引發(fā) int 類型和 Python 對(duì)象之間的轉(zhuǎn)換。

Numpy

使用擁有大量數(shù)據(jù)的多維數(shù)組或記錄數(shù)組會(huì)占用大量?jī)?nèi)存。但是,為了有效地利用純 Python 處理數(shù)據(jù),你應(yīng)該使用 Numpy 包提供的函數(shù)。

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

一個(gè)擁有 N 個(gè)元素、初始化成零的數(shù)組可以通過下面的函數(shù)創(chuàng)建:

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

內(nèi)存占用是最小的:

如何用Python代碼減少Python所需的內(nèi)存

一般情況下,訪問數(shù)組元素和行會(huì)引發(fā) Python 對(duì)象與 C 語言 int 值之間的轉(zhuǎn)換。如果從生成的數(shù)組中獲取一行結(jié)果,其中包含一個(gè)元素,其內(nèi)存就沒那么緊湊了:

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

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

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注創(chuàng)新互聯(lián)-成都網(wǎng)站建設(shè)公司行業(yè)資訊頻道,感謝您對(duì)創(chuàng)新互聯(lián)的支持。


分享題目:如何用Python代碼減少Python所需的內(nèi)存-創(chuàng)新互聯(lián)
網(wǎng)站網(wǎng)址:http://weahome.cn/article/jpocc.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部