子类化namedtuple与实现__slots __?

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

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

在寻找节省内存的过程中,我遇到了__slots__,它阻止类为任意属性分配创建__dict__。这很棒,因为我知道对象将具有什么属性,所以我可以将其添加到:

class Extent:
    __slots__ =  ('xmin','ymin','xmax','ymax','center')
    def __init__(self,ymax):
        ...

然后我开始探索namedtuple,因为它们占用的内存非常小,缺点是除非我将它们子类化,否则我无法添加方法

Boundary = namedtuple('Boundary',['xmin','center'])
class Extent(Boundary):
    def contains(self,extent: 'Extent'):
        ...
    def intersects(self,extent: 'Extent'):
        ...

但这仍然允许创建__dict__,因此我们必须明确声明__slots__

Boundary = namedtuple('Boundary','center'])
class Extent(Boundary):
    __slots__ = ()
    ...

所以我的问题是,上一个示例(带有namedtuple的{​​{1}}子类)比实现__slots__并且不继承任何东西的类有什么好处? / p>

zgyanfeng 回答:子类化namedtuple与实现__slots __?

我将让您的namedtuple x插槽这个特定问题分开,然后集中讨论您的内存节省问题。因为元组x带槽的属性可能并且大小会有所不同,但会有所不同-但是使用这两个属性时,必须保留完整的Python对象作为数字本身。

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

如果懒惰地获取数字会导致性能问题,则可以将类移至cython,并让操作使用每个对象中的本机压缩数字。

总而言之,有效的方法可能是专门的Sequence类,它将包装NumyArray以及序列中对象的数据以及从该序列中作为元素检索的短暂对象。

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

下面的代码可以在numpy数组中保存一个“ Extents”类,每个实例恰好占用了数据所需的字节数。优点是可以使用扩展的numpy数据类型-例如,如果需要考虑内存并且32bit FP值足以满足您的需要,请使用“ float32”(“ f4”)。

您实际上可以只使用带有自定义dtype的原始np数组-此代码显示了如何添加一些钟声来具有您的“ center”属性,并能够访问xmin等作为属性,比仅使用映射语法(seq [0] [“ xmin”]):

from collections.abc import MutableSequence

import numpy as np


class Extent(np.void):
    attrs = "xmin ymin xmax ymax".split()
    dtype = np.dtype([(attr,"f8") for attr in attrs])

    def __getattr__(self,attr):
        return self.__getitem__(attr)

    def __setattr__(self,attr,value):
        if value in self.attrs:
            return self.__setitem__(self,value)
        return super().__setattr__(attr,value)

    @classmethod
    def store(self,storage,xmin,ymin,xmax,ymax):
        storage.append((xmin,ymax))
        return storage[storage.last_item - 1]

    def contains(self,extent: 'Extent'):
        pass
        # checks if one contains the other
    def intersects(self,extent: 'Extent'):
        pass
        # checks if one intersects the other

    @property
    def center(self):
        return ((self.xmin + self.xmax) / 2,(self.ymin + self.ymax) / 2)

    def __repr__(self):
        return f"Extent <{self.xmin},{self.ymin},{self.xmax},{self.ymax}>"


class ExtentList:
    def __init__(self,max_size):
        self.last_item = 0
        self.data = np.zeros(max_size,dtype=Extent)

    def __getitem__(self,index):
        return  self.data[index]

    def __setitem__(self,index,value):
        self.data[index] = value

    def append(self,extent):
        self.data[self.last_item] = extent
        self.last_item += 1

    def __iter__(self):
        for i in range(self.last_item):
            yield self.data[i]

    def __repr__(self):
        return f"ExtentList <{self.data[:self.max_size]!r}>,max={self.max_size}"


在交互式终端上:


In [63]: values = ExtentList(10)                                                                                                     

In [64]: v = Extent.store(values,10,20,20)                                                                                    

In [65]: v                                                                                                                           
Out[65]: Extent <10.0,10.0,20.0,20.0>

In [66]: v.center                                                                                                                    
Out[66]: (15.0,15.0)

此外-这种方法允许您的属性就地可变:

In [73]: v.xmax = 40                                                                                                                 

In [74]: v.center                                                                                                                    
Out[74]: (25.0,15.0)

唯一的缺点是您无法轻松调整numpy数组的大小-因此,我需要为结尾保留一个内部索引,而您必须设置max-size。如果max-size的变化太大,围绕如何调整numpy-array的大小的方法很多,您必须将逻辑添加到容器类中才能做到这一点,这受Python本身用于分配大小的逻辑的启发列表。

本文链接:https://www.f2er.com/3135514.html

大家都在问