我正在开发一个在运行时创建数万个小型Python对象的应用程序。不幸的是,Python对象因占用大量RAM而臭名昭著。我发现了这篇有关如何解决该问题的有趣文章:
http://www.qtrac.eu/pysavemem.html
本文提供了一些有用的技巧,但并未完全解释它们。我不能为某些建议的解决方案而烦恼。请帮助我获得见识。我们将一一介绍。
本文的基线示例是简单的Rect
类:
class Rect:
def __init__(self, x1, y1, x2, y2):
self.x1 = x1
self.x2 = x2
self.y1 = y1
self.y2 = y2
在运行64位Python 3的64位计算机上,此对象将消耗400KB。
__slots__
技术是迄今为止最简单的优化。这是文章中的示例:
class Rect:
__slots__ = ("x1", "x2", "y1", "y2")
def __init__(self, x1, y1, x2, y2):
self.x1 = x1
self.x2 = x2
self.y1 = y1
self.y2 = y2
必须事先声明对象的属性x1
,x2
,y1
和y2
。您不能向此类创建的对象添加任意额外的数据。
这些实例仅消耗212KB RAM。内存大小几乎减少了50%。
到目前为止,Rect()
实例将分别生成四个内部对象:x1
,x2
,y1
和y2
。随后的新技术尝试做不同的事情。代替了四个对象,只创建了一个单个 Python对象:
class Rect:
__slots__ = ("_data",)
# We are not limited to using the same types; could mix any
# fixed-width types we want. And, of course, we can add extra
# items to the struct later if need be.
Coords = struct.Struct("llll")
def __init__(self, x1, y1, x2, y2):
self._data = Rect.Coords.pack(x1, y1, x2, y2)
@property
def x1(self):
return Rect.Coords.unpack(self._data)[0]
@property
def x2(self):
return Rect.Coords.unpack(self._data)[1]
@property
def y1(self):
return Rect.Coords.unpack(self._data)[2]
@property
def y2(self):
return Rect.Coords.unpack(self._data)[3]
该文章指出,消耗的内存现在只有137KB。但是,它没有解释如何。我不能把头缠在某些表情上:
__slots__ = ("_data",)
的实际作用是什么?
Coords
是类成员,而不是实例成员。那么,如何通过这种方式为每个实例获取不同的数据?
这些pack()
和unpack()
方法实际上是做什么的?
"llll"
的{{1}}的参数是否表示Struct()
,x1
,x2
和y1
的输入y2
?
该文章说该示例也可以扩展为具有可写属性。看起来如何?
最后,本文提供了类似的解决方案,但代码较短:
long
这个解决方案对我来说还不清楚...
您为解释这些优化技术所做的努力将不胜感激!如果适用,请随时提出其他解决方案。就个人而言,我使用最新的Python 3.7版本。
答案 0 :(得分:0)
在recordclass库的基础上还有另一种方法:
from recordclass import dataobject
class Rectangle(dataobject):
x1:int
x2:int
y1:int
y2:int
此解决方案所需的内存少于基于__slots__
的解决方案。差异等于PyGC_Head
的大小(在64位平台上为24字节)。与基于__slots__
的解决方案相比,它可能还具有更快的实例创建路径:
class Rectangle(dataobject):
x1:int
x2:int
y1:int
y2:int
__options__ = {'argsonly':True}