日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

python减少内存_如何降低 Python 的内存消耗量?

發布時間:2023/11/27 生活经验 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python减少内存_如何降低 Python 的内存消耗量? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

👆“Python貓” ,一個值得加星標的公眾號

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

圖 | 《借東西的小人阿莉埃蒂》劇照

作者 |?intellimath

譯者 |?彎月,責編 | 郭芮

出品 | CSDN(ID:CSDNnews)

以下為譯文:

為了簡便起見,我們以一個表示點的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占用了大量內存,尤其是如果突然虛需要創建大量實例時:

實例數

對象大小

1 000 000

240 Mb

10 000 000

2.40 Gb

100 000 000

24 Gb

類實例

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

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

類實例的結構很有趣:

字段

大小(比特)

PyGC_Head

24

PyObject_HEAD

16

__weakref__

8

__dict__

8

合計:

56

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

>>>?print(sys.getsizeof(ob),?sys.getsizeof(ob.__dict__))

56?112

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

實例數

大小

1 000 000

168 Mb

10 000 000

1.68 Gb

100 000 000

16.8 Gb

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

帶有__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

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

字段

大小(比特)

PyGC_Head

24

PyObject_HEAD

16

x

8

y

8

z

8

總計:

64

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

實例數

大小

1 000 000

64 Mb

10 000 000

640 Mb

100 000 000

6.4 Gb

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

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

>>>?pprint(Point.__dict__)

mappingproxy(

....................................

'x':?,

'y':?,

'z':?})

為了自動化使用__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__的類:

字段

大小(字節)

PyGC_Head

24

PyObject_HEAD

16

ob_size

8

[0]

8

[1]

8

[2]

8

總計:

72

命名元組

由于元組的使用非常廣泛,所以終有一天你需要通過名稱訪問元組。為了滿足這種需求,你可以使用模塊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))

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

實例數

大小

1 000 000

72 Mb

10 000 000

720 Mb

100 000 000

7.2 Gb

記錄類:不帶循環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:

字段

大小(字節)

PyObject_HEAD

16

ob_size

8

x

8

y

8

z

8

總計:

48

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

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

實例數

大小

1 000 000

48 Mb

10 000 000

480 Mb

100 000 000

4.8 Gb

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:

字段

大小(字節)

PyObject_HEAD

16

ob_size

8

x

8

y

8

z

8

總計:

48

>>>?ob?=?Point(1,2,3)

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

40

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

mappingproxy({'__new__':?,

.......................................

'x':?,

'y':?,

'z':?})

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

實例數

大小

1 000 000

40 Mb

10 000 000

400 Mb

100 000 000

4.0 Gb

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

內存結構如下:

字段

大小(字節)

PyObject_HEAD

16

x

4

y

4

z

4

nycto

4

總計:

32

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

實例數

大小

1 000 000

32 Mb

10 000 000

320 Mb

100 000 000

3.2 Gb

但是,需要記住在從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)

內存占用是最小的:

實例數

大小

1 000 000

12 Mb

10 000 000

120 Mb

100 000 000

1.2 Gb

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

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

68

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

總結

在本文中,我們通過一個簡單明了的例子,求證了Python語言(CPython)社區的開發人員和用戶可以真正減少對象占用的內存量。

原文:https://habr.com/en/post/458518

本文為 CSDN 翻譯,【Python貓】授權轉載。

文章分享完了,最后是隨機薦書環節。我會根據文章內容,提出一個關鍵詞(這篇是“numpy”)來搜索,隨機選擇一本技術書推薦給大家,如果你感興趣的話,可以點擊鏈接進行了解。希望能給大家帶來不經意的收獲~~

告訴朋友們,我在看

總結

以上是生活随笔為你收集整理的python减少内存_如何降低 Python 的内存消耗量?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。