Python 子类化namedtuple与实现_插槽_;?

Python 子类化namedtuple与实现_插槽_;?,python,class,oop,python-internals,Python,Class,Oop,Python Internals,我目前正在尝试优化一个程序,它可以从较小的内存占用中获益。在该程序中是存储数据且永远不会更改其状态的对象,但它们确实具有功能,例如: class Extent: def __init__(self, xmin, ymin, xmax, ymax): self.xmin = xmin self.ymin = ymin self.xmax = xmax self.ymax = ymax self.center =

我目前正在尝试优化一个程序,它可以从较小的内存占用中获益。在该程序中是存储数据且永远不会更改其状态的对象,但它们确实具有功能,例如:

class Extent:
    def __init__(self, xmin, ymin, xmax, ymax):
        self.xmin = xmin
        self.ymin = ymin
        self.xmax = xmax
        self.ymax = ymax
        self.center = ((xmin + xmax) / 2, (ymin + ymax) / 2)
    def contains(self, extent: 'Extent'):
        # checks if one contains the other
    def intersects(self, extent: 'Extent'):
        # checks if one intersects the other
在我寻找内存节省的过程中,我遇到了
\uuuuuu slots\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
,它阻止类为任意属性分配创建
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuu。这很好,因为我知道对象将具有哪些属性,所以我可以将其添加到:

class Extent:
    __slots__ =  ('xmin', 'ymin', 'xmax', 'ymax', 'center')
    def __init__(self, xmin, ymin, xmax, ymax):
        ...
然后,我开始探索
namedtuple
,因为它们使用的内存占用非常小,缺点是除非对它们进行子类化,否则无法添加方法:

Boundary = namedtuple('Boundary', ['xmin', 'ymin', 'xmax', 'ymax', 'center'])
class Extent(Boundary):
    def contains(self, extent: 'Extent'):
        ...
    def intersects(self, extent: 'Extent'):
        ...
但这仍然允许创建
\uuuuu dict\uuuuu
,因此我们必须明确说明
\uuuuu slots\uuuuu

Boundary = namedtuple('Boundary', ['xmin', 'ymin', 'xmax', 'ymax', 'center'])
class Extent(Boundary):
    __slots__ = ()
    ...

因此,我的问题是,上一个示例(
namedtuple
带有
\uuuuuu slots\uuuuuu
的子类)与实现
\uuuuu slots\uuuuu
且不继承任何内容的类相比,有什么好处?

我将让您具体的namedtuple x slots问题分开,并关注您的内存节省问题。因为元组x有槽属性可能会不同,并且在大小上会有几个字节的差异——但是通过使用这两种方法,您必须将完整的Python对象作为数字本身来保存

有一个类可以将数据保存为数组中的压缩字节,并根据需要惰性地将属性生成为Python数字。内存方面,这将是一个赢家,因为Python中的数字是最小大小为24字节的完整对象(在64位平台上)

如果延迟获取数字成为性能问题,您可以将类移动到cython,并让操作在每个对象中使用本机压缩的数字

总而言之,一种有效的方法可能是一个专门的Sequence类,它将用序列中对象的数据和从该序列中检索为元素的临时对象包装一个NumyArray

好消息是numpy已经提供了这一功能——它允许数组由具有自定义
dtype
的对象组成,甚至还有
np.void
一个由其dtype定义的结构类型基类——它可以具有额外的功能

下面的代码可以在numpy数组中保存一个“Extents”类,每个实例正好获取数据所需的字节数。其优点是可以使用扩展的numpy数据类型—例如,如果内存是一个问题,并且32位FP值对您来说已经足够,则可以使用“float32”(“f4”)

实际上,您可以使用一个带有自定义数据类型的原始np数组——这段代码展示了如何添加一些bell和whistles来拥有“center”属性,并能够将xmin等作为属性访问,而不仅仅是使用映射语法(seq[0][“xmin”]):

from collections.abc导入可变序列
将numpy作为np导入
类别范围(np.void):
attrs=“xmin-ymin-xmax-ymax”.split()
dtype=np.dtype([(attr,“f8”)表示attr中的attr])
def _ugetattr _;(self,attr):
返回自我。获取项目(属性)
定义设置属性(自身、属性、值):
如果self.attrs中的值为:
返回self.\uuuu setitem\uuuu(self,attr,value)
返回super()。\uuuu setattr\uuuu(attr,value)
@类方法
def存储(自存储、存储、xmin、ymin、xmax、ymax):
存储追加((xmin,ymin,xmax,ymax))
返回存储[storage.last_item-1]
def包含(自身,范围:“范围”):
通过
#检查其中一个是否包含另一个
def相交(self,extent:“extent”):
通过
#检查一个是否与另一个相交
@财产
def中心(自我):
返回((self.xmin+self.xmax)/2,(self.ymin+self.ymax)/2)
定义报告(自我):
返回f“范围”
类扩展列表:
def_uuu初始(自身,最大尺寸):
self.last_项=0
self.data=np.zero(最大大小,数据类型=范围)
定义uu获取项目uu(自身,索引):
返回self.data[索引]
定义设置项(自身、索引、值):
self.data[索引]=值
def追加(自身、范围):
self.data[self.last_item]=范围
self.last_项目+=1
定义(自我):
对于范围内的i(自身最后一项):
收益率自我数据[i]
定义报告(自我):
返回f“ExtentList,max={self.max_size}”
在交互式终端上:


在[63]中:值=扩展列表(10)
In[64]:v=扩展存储(值,10,10,20,20)
In[65]:v
Out[65]:范围
In[66]:v.center
Out[66]:(15.0,15.0)
此外,这种方法允许您的属性在适当的位置可变:

[73]中的
v.xmax=40
在[74]中:v.center
Out[74]:(25.0,15.0)

ony的缺点是无法轻松调整numpy数组的大小-因此,我保留了一个内部索引作为结尾,并且必须设置最大大小。如果最大大小变化太大,那么关于如何调整numpy数组的大小有很多方法,您必须将逻辑添加到容器类中才能做到这一点,灵感来自Python本身用于为列表分配大小的逻辑。

嗯,
namedtuple
是一个
元组。所以如果你想要一个序列类型,这是一个很好的方法。否则,您可能只需要一个带有插槽的常规类。@juanpa.ar