__slots__节约空间

1.为什么要使用__slots__

Python 使用 dicts(hash table)缓存大量的静态资源(属性)。

我们最近在Image类中,用仅仅一行__slots__代码,改变成使用tuple储存,可以节约大量内存空间。

为啥呢?

和list相比,dict 查找和插入的速度极快,不会随着key的增加而增加;dict需要占用大量的内存,内存浪费多。

而list查找和插入的时间随着元素的增加而增加;占用空间小,浪费的内存很少。

python解释器是Cpython,这两个数据结构应该对应C的哈希表和数组。因为哈希表需要额外内存记录映射关系,而数组只需要通过索引就能计算出下一个节点的位置,所以哈希表占用的内存比数组大,也就是dict比list占用的内存更大。

2.默认情况

默认情况下,Python用一个dict来存储对象实例的属性。这在一般情况下还不错,而且非常灵活,乃至你在运行时可以随意设置新的属性。

但是,对一些在”编译”前就知道该有几个固定属性的小class来说,这个dict就有点浪费内存了。而当你把这个小浪费乘上一百万,那可就大不同了。

在Python中,你可以在class中设置__slots__,它是一个包含这些固定的属性名的list。这样Python就不会再使用dict,而且只分配这些属性的空间。

默认情况下:

改成__slots__后:

class Image(object):
    __slots__ = ['id', 'name', 'age']
 
    def __init__(self, id, name, age):
        self.id = id
        self.name = name
        self.age = age
        
        
        
i=Image(1,'wqbin',18)
i.__dict__

你还可以用collections.namedtuple,它允许访问参数,但只占用一个tuple的空间。这跟__slots__类似。不过我总觉得继承一个namedtuple类很奇怪。另外,如果你需要自定义初始化,你应该重载__new__而不是__init__。

警告:不要贸然进行这个优化,把它用在所有地方。这种做法不利于代码维护,而且只有当你有数以千计的实例的时候才会有明显效果。

译注:作者在评论中关于”不利于代码维护“的说法:

webreac:我觉得__slots__关键字不只是速度优化(注:这里应该是内存优化),也是类字段名的一个可靠”文档“。这有利于代码维护。为什么你觉得它不好?

Ben Hoyt(作者):有趣的说法——我不确定应不应该把__slots__作为文档。不过的确是不错的注意。我之前这么说的原因是,你需要对字段名”定义“两次(不够DRY)。namedtuple也类似

 3.空间节省效果

如何查看内存占用

class Foobar(object):
    __slots__=('x')
    def __init__(self, x):
        self.x = x
 
@profile
def main():
    f = [Foobar(42) for i in range(1000000)]
 
if __name__ == "__main__":
    main()

 

class Foobar(object):
    def __init__(self, x):
        self.x = x
 
@profile
def main():
    f = [Foobar(42) for i in range(1000000)]
 
if __name__ == "__main__":
    main()

 

 

100百万个实例化对象的内存占用从208M降低到93M

 

posted @ 2019-02-18 11:29  wqbin  阅读(214)  评论(0)    收藏  举报