Python占用内存如何优化-创新互联

这篇文章主要为大家展示了“Python占用内存如何优化”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Python占用内存如何优化”这篇文章吧。

目前成都创新互联公司已为1000+的企业提供了网站建设、域名、网络空间、绵阳服务器托管、企业网站设计、屯留网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。

举个栗子

我们举个简单的场景,使用Python存储一个三维坐标数据,x,y,z。

Dict

使用Python内置的数据结构Dict来实现上述例子的需求很简单。

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

查看以下ob这个对象占用的内存大小:

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

简单的三个整数,占用的内存还真不少,想象以下,如果有大量的这样的数据要存储,会占用更大的内存。

数据量占用内存大小
1 000 000240 Mb
10 000 0002.40 Gb
100 000 00024 Gb

Class

对于喜欢面向对象编程的程序员来说,更喜欢把数据包在一个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区别就很大了,我们来看看这种情况下占用内存的情况:

字段占用内存
PyGC_Head24
PyObject_HEAD16
__weakref__8
__dict__8
TOTAL56

关于 __weakref__(弱引用)可以查看这个文档, 对象的dict中存储了一些self.xxx的一些东西。从Python 3.3开始,key使用了共享内存存储, 减少了RAM中实例跟踪的大小。

>>> print(sys.getsizeof(ob), sys.getsizeof(ob.__dict__)) 
56 112
数据量占用内存
1 000 000168 Mb
10 000 0001.68 Gb
100 000 00016.8 Gb

可以看到内存占用量,class比dict少了一些,但这远远不够。

__slots__

从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

可以看到内存占用显著的减少了

字段内存占用
PyGC_Head24
PyObject_HEAD16
x8
y8
z8
TOTAL64
数据量占用内存
1 000 00064Mb
10 000 000640Mb
100 000 0006.4Gb

默认情况下,Python的新式类和经典类的实例都有一个dict来存储实例的属性。这在一般情况下还不错,而且非常灵活,乃至在程序中可以随意设置新的属性。但是,对一些在”编译”前就知道有几个固定属性的小class来说,这个dict就有点浪费内存了。

当需要创建大量实例的时候,这个问题变得尤为突出。一种解决方法是在新式类中定义一个slots属性。

slots声明中包含若干实例变量,并为每个实例预留恰好足够的空间来保存每个变量;这样Python就不会再使用dict,从而节省空间。

那么用slot就是非非常那个有必要吗?使用slots也是有副作用的:

  1. 每个继承的子类都要重新定义一遍slots

  2. 实例只能包含哪些在slots定义的属性,这对写程序的灵活性有影响,比如你由于某个原因新网给instance设置一个新的属性,比如instance.a = 1, 但是由于a不在slots里面就直接报错了,你得不断地去修改slots或者用其他方法迂回的解决

  3. 实例不能有弱引用(weakref)目标,否则要记得把weakref放进slots

最后,namedlist和attrs提供了自动创建带slot的类,感兴趣的可以试试看。

Tuple

Python还有一个内置类型元组,用于表示不可变数据结构。 元组是固定的结构或记录,但没有字段名称。 对于字段访问,使用字段索引。 在创建元组实例时,元组字段一次性与值对象关联:

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

元组的示例很简洁:

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

可以看只比slot多8byte:

字段占用内存(bytes)
PyGC_Head24
PyObject_HEAD16
ob_size8
[0]8
[1]8
[2]8
TOTAL72

Namedtuple

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

此类的所有实例都具有与元组相同的内存占用。 大量实例会留下稍大的内存占用:

数据量内存占用
1 000 00072 Mb
10 000 000720 Mb
100 000 0007.2 Gb

Recordclass

python的第三方库recordclassd提供了一个数据结构recordclass.mutabletuple,它几乎和内置tuple数据结构一致,但是占用更少的内存。

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

实例化以后,只少了PyGC_Head:

字段占用内存
PyObject_HEAD16
ob_size8
x8
y8
y8
TOTAL48

到此,我们可以看到,和slot比,又进一步缩小了内存占用:

数据量内存占用
1 000 00048 Mb
10 000 000480 Mb
100 000 0004.8 Gb

Dataobject

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:

字段内存占用(bytes)
PyObject_HEAD16
x8
y8
y8
TOTAL40
>>> ob = Point(1,2,3)
>>> print(sys.getsizeof(ob))
40

要访问这些字段,还使用特殊描述符通过其从对象开头的偏移量来访问字段,这些对象位于类字典中:

mappingproxy({'__new__': ,
    .......................................
    'x': ,
    'y': ,
    'z': })
数据量内存占用
1 000 00040 Mb
10 000 000400 Mb
100 000 0004.0 Gb

Cython

有一种方法基于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

内存结构分布如下:

字段内存占用(bytes)
PyObject_HEAD16
x4
y4
y4
пусто4
TOTAL32
数据量内存占用
1 000 00032 Mb
10 000 000320 Mb
100 000 0003.2 Gb

但是,从Python代码访问时,每次都会执行从int到Python对象的转换,反之亦然。

Numpy

在纯Python的环境中,使用Numpy能带来更好的效果,例如:

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

创建初始值是0的数组:

>>> points = numpy.zeros(N, dtype=Point)
数据量内存占用
1 000 00012 Mb
10 000 000120 Mb
100 000 0001.2 Gb

最后

可以看出,在Python性能优化这方面,还是有很多事情可以做的。Python提供了方便的同时,也需要暂用较多的资源。在不通的场景下,我需要选择不同的处理方法,以便带来更好的性能体验。

以上是“Python占用内存如何优化”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注创新互联成都网站设计公司行业资讯频道!

另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


当前标题:Python占用内存如何优化-创新互联
URL地址:http://pwwzsj.com/article/ddchsi.html