今天就跟大家聊聊有关Python中怎么优化占用的内存,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
使用Python内置的数据结构Dict来实现上述例子的需求很简单。
>>> ob = {'x':1, 'y':2, 'z':3} >>> x = ob['x'] >>> ob['y'] = y
查看以下ob这个对象占用的内存大小:
>>> print(sys.getsizeof(ob)) 240
简单的三个整数,占用的内存还真不少,想象以下,如果有大量的这样的数据要存储,会占用更大的内存。
对于喜欢面向对象编程的程序员来说,更喜欢把数据包在一个class里。使用class使用同样需求:
class Point: # def __init__(self, x, y, z): self.x = x self.y = y self.z = z >>> ob = Point(1,2,3)
class的数据结构和Dict区别就很大了,我们来看看这种情况下占用内存的情况:
关于 _weakref_(弱引用)可以查看这个文档, 对象的__dict__中存储了一些self.xxx的一些东西。从Python 3.3开始,key使用了共享内存存储, 减少了RAM中实例跟踪的大小。
>>> print(sys.getsizeof(ob), sys.getsizeof(ob.__dict__)) 56 112
可以看到内存占用量,class比dict少了一些,但这远远不够。
从class的内存占用分布上,我们可以发现,通过消除__dict__和_weakref__,可以显着减少RAM中类实例的大小,我们可以通过使用__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的新式类和经典类的实例都有一个dict来存储实例的属性。这在一般情况下还不错,而且非常灵活,乃至在程序中可以随意设置新的属性。但是,对一些在”编译”前就知道有几个固定属性的小class来说,这个dict就有点浪费内存了。
当需要创建大量实例的时候,这个问题变得尤为突出。一种解决方法是在新式类中定义一个__slots__属性。
__slots__声明中包含若干实例变量,并为每个实例预留恰好足够的空间来保存每个变量;这样Python就不会再使用dict,从而节省空间。
那么用slot就是非非常那个有必要吗?使用__slots__也是有副作用的:
每个继承的子类都要重新定义一遍__slots__
实例只能包含哪些在__slots__定义的属性,这对写程序的灵活性有影响,比如你由于某个原因新网给instance设置一个新的属性,比如instance.a = 1, 但是由于a不在__slots__里面就直接报错了,你得不断地去修改__slots__或者用其他方法迂回的解决
实例不能有弱引用(weakref)目标,否则要记得把__weakref__放进__slots__
最后,namedlist和attrs提供了自动创建带__slot__的类,感兴趣的可以试试看。
Python还有一个内置类型元组,用于表示不可变数据结构。 元组是固定的结构或记录,但没有字段名称。 对于字段访问,使用字段索引。 在创建元组实例时,元组字段一次性与值对象关联:
>>> ob = (1,2,3) >>> x = ob[0] >>> ob[1] = y # ERROR
元组的示例很简洁:
>>> print(sys.getsizeof(ob)) 72
可以看只比__slot__多8byte:
通过namedtuple我们也可以实现通过key值来访问tuple里的元素:
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_y(self): return self[2] # def __new__(cls, x, y, z): return tuple.__new__(cls, (x, y, z))
此类的所有实例都具有与元组相同的内存占用。 大量实例会留下稍大的内存占用:
python的第三方库recordclassd提供了一个数据结构recordclass.mutabletuple,它几乎和内置tuple数据结构一致,但是占用更少的内存。
>>> Point = recordclass('Point', ('x', 'y', 'z')) >>> ob = Point(1, 2, 3)
实例化以后,只少了PyGC_Head:
到此,我们可以看到,和__slot__比,又进一步缩小了内存占用:
recordclass提供了另外一个解决方法:在内存中使用与__slots__类相同的存储结构,但不参与循环垃圾收集机制。通过recordclass.make_dataclass可以创建出这样的实例:
>>> Point = make_dataclass('Point', ('x', 'y', 'z'))
另外一个方法是继承自dataobject
class Point(dataobject): x:int y:int z:int
以这种方式创建的类将创建不参与循环垃圾收集机制的实例。 内存中实例的结构与__slots__的情况相同,但没有PyGC_Head:
>>> 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>})
有一种方法基于Cython的使用。 它的优点是字段可以采用C语言原子类型的值。例如:
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代码访问时,每次都会执行从int到Python对象的转换,反之亦然。
在纯Python的环境中,使用Numpy能带来更好的效果,例如:
>>> Point = numpy.dtype(('x', numpy.int32), ('y', numpy.int32), ('z', numpy.int32)])
创建初始值是0的数组:
>>> points = numpy.zeros(N, dtype=Point)
看完上述内容,你们对Python中怎么优化占用的内存有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注亿速云行业资讯频道,感谢大家的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。