浅谈Python 对象内存占用
一切皆是对象
在Python一切皆是对象,包括所有类型的常量与变量,整型,布尔型,甚至函数。参见stackoverflow上的一个问题Iseverythinganobjectinpythonlikeruby
代码中即可以验证:
#everythininpythonisobjectdeffuction():returnprintisinstance(True,object)printisinstance(0,object)printisinstance('a',object)printisinstance(fuction,object)
如何计算
Python在sys模块中提供函数getsizeof来计算Python对象的大小。
sys.getsizeof(object[,default]) 以字节(byte)为单位返回对象大小。这个对象可以是任何类型的对象。所以内置对象都能返回正确的结果但不保证对第三方扩展有效,因为和具体实现相关。 ...... getsizeof()调用对象的__sizeof__方法,如果对象由垃圾收集器管理,则会加上额外的垃圾收集器开销。
当然,对象内存占用与Python版本以及操作系统版本关系密切,本文的代码和测试结果都是基于windows732位操作系统。
基本类型
•布尔型
print'sizeofTrue:%d'%(sys.getsizeof(True))print'sizeofFalse:%d'%(sys.getsizeof(False))
输出:
sizeofTrue:12sizeofFalse:12
•整型
#normalintegerprint'sizeofinteger:%d'%(sys.getsizeof(1))#longprint'sizeoflonginteger:%d'%(sys.getsizeof(1L))print'sizeofbiglonginteger:%d'%(sys.getsizeof(100000L))输出:
sizeofinteger:12xsizeoflonginteger1L:14sizeoflonginteger100000L:16
可以看出整型占用12字节,长整型最少占用14字节,且占用空间会随着位数的增多而变大。在2.x版本,如果整型类型的值超出sys.maxint,则自动会扩展为长整型。而Python3.0之后,整型和长整型统一为一种类型。
•浮点型
print'sizeoffloat:%d'%(sys.getsizeof(1.0))
输出:
sizeoffloat:16
浮点型占用16个字节。超过一定精度后会四舍五入。
参考如下代码:
print1.00000000003print1.000000000005
输出:
1.000000000031.00000000001
•字符串
#sizeofstringtypeprint'\r\n'.join(["sizeofstringwith%dchars:%d"%(len(elem),sys.getsizeof(elem))forelemin["","a","ab"]])#sizeofunicodestringprint'\r\n'.join(["sizeofunicodestringwith%dchars:%d"%(len(elem),sys.getsizeof(elem))forelemin[u"",u"a",u"ab"]])
输出:
sizeofstringwith0chars:21sizeofstringwith1chars:22sizeofstringwith2chars:23sizeofunicodestringwith0chars:26sizeofunicodestringwith1chars:28sizeofunicodestringwith2chars:30
普通空字符串占21个字节,每增加一个字符,多占用1个字节。Unicode字符串最少占用26个字节,每增加一个字符,多占用2个字节。
集合类型
•列表
#sizeoflisttypeprint'\r\n'.join(["sizeoflistwith%delements:%d"%(len(elem),sys.getsizeof(elem))forelemin[[],[0],[0,2],[0,1,2]]])
输出:
sizeoflistwith0elements:36sizeoflistwith1elements:40sizeoflistwith2elements:44sizeoflistwith3elements:48
可见列表最少占用36个字节,每增加一个元素,增加4个字节。但要注意,sys.getsizeof 函数并不计算容器类型的元素大小。比如:
print'sizeoflistwith3integers%d'%(sys.getsizeof([0,1,2]))print'sizeoflistwith3strings%d'%(sys.getsizeof(['0','1','2']))
输出:
sizeoflistwith3integers48sizeoflistwith3strings48
容器中保存的应该是对元素的引用。如果要准确计算容器,可以参考recursivesizeofrecipe 。使用其给出的 total_size 函数:
print'totalsizeoflistwith3integers%d'%(total_size([0,1,2]))print'totalsizeoflistwith3strings%d'%(total_size(['0','1','2']))
输出为:
totalsizeoflistwith3integers84totalsizeoflistwith3strings114
可以看出列表的空间占用为基本空间36+(对象引用4+对象大小)*元素个数。
另外还需注意如果声明一个列表变量,则其会预先分配一些空间,以便添加元素时增加效率:
li=[]foriinrange(0,101):print'listwith%dintegerssize:%d,total_size:%d'%(i,getsizeof(li),total_size(li))li.append(i)
•元组
基本与列表类似,但其最少占用为28个字节。
•字典
字典的情况相对复杂很多,具体当然要参考代码dictobject.c,另外NOTESONOPTIMIZINGDICTIONARIES非常值得仔细阅读。
基本情况可以参考[stackoverflow]的问题Python'sunderlyinghashdatastructurefordictionaries中的一些回答:
•字典最小拥有8个条目的空间(PyDict_MINSIZE);
•条目数小于50,000时,每次增长4倍;
•条目数大于50,000时,每次增长2倍;
•键的hash值缓存在字典中,字典调整大小后不会重新计算;
每接近2/3时,字典会调整大小。
以上这篇浅谈Python对象内存占用就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。