zoukankan      html  css  js  c++  java
  • Python tricks(7) -- new-style class的__slots__属性

    __slots__是在python 2.2开始引入的一个新特性, 我们来看一下官方给出的解释.

    This class variable can be assigned a string, iterable, or sequence of strings with variable names used by instances. If defined in a new-style class, __slots__ reserves space for the declared variables and prevents the automatic creation of __dict__ and__weakref__ for each instance.

    为声明的字段保留空间, 不再为每个实例生成一个__dict__和__weakref__. [参考文献1]

    python默认定义的class是可以动态添加属性的, 代码示例如下

    In [1]: class Test(object):
       ...:     pass
       ...:
    
    In [2]: a = Test()
    
    In [3]: a.a = 1
    
    In [4]: a.a
    Out[4]: 1
    

    可以动态地为a添加一个属性, 在class中会默认创建一个特殊的对象__dict__, 负责保存class的所有属性和方法.

    这个__slots__主要的一个作用是减少了对内存的消耗, 这个尤其在对象数量较多的时候非常管用, 可以查看[参考文献3], 使用__slots__未作任何其他优化, 节省了9G内存.

    示例代码如下

    import sys
    from guppy import hpy
    
    
    class Person_(object):
        __slots__ = ("name", "age", "gender")
    
        def __init__(self):
            pass
    
    
    class Person(object):
        def __init__(self):
            pass
    
    
    if __name__ == "__main__":
        persons = []
        for i in xrange(100000):
            p = Person()
            p.name = "name_%d" % i
            p.age = i
            p.gender = "female"
            persons.append(p)
    
        persons_ = []
        for i in xrange(100000):
            p = Person_()
            p.name = "name_%d" % i
            p.age = i
            p.gender = "female"
            persons_.append(p)
    
        print "size without slots: %d" % sum([sys.getsizeof(p) for p in persons])
        print "size of the __dict__ without slots: %d" % sum([sys.getsizeof(p.__dict__) for p in persons])
        print "size of the __weakref__ without slots: %d" % sum([sys.getsizeof(p.__weakref__) for p in persons])
        print "size with slots: %d" % sum([sys.getsizeof(p) for p in persons_])
    
        h = hpy()
        print h.heap()
    

    程序输出结果如下:

    size without slots: 3200000
    size of the __dict__ without slots: 14000000
    size of the __weakref__ without slots: 800000
    size with slots: 3600000
    Partition of a set of 739737 objects. Total size = 32889732 bytes.
     Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
         0 100000  14 14000000  43  14000000  43 dict of __main__.Person
         1 214673  29  7335252  22  21335252  65 str
         2 100000  14  3600000  11  24935252  76 __main__.Person_
         3 100000  14  3200000  10  28135252  86 __main__.Person
         4 209137  28  2509644   8  30644896  93 int
         5    181   0   831828   3  31476724  96 list
         6   7627   1   339356   1  31816080  97 tuple
         7    331   0   232004   1  32048084  97 dict (no owner)
         8   1670   0   120240   0  32168324  98 types.CodeType
         9     73   0   110060   0  32278384  98 dict of module
    <95 more rows. Type e.g. '_.more' to view.>
    

    获取内存大小的两种方法: 一种使用sys.getsizeof()方法, 一种可以采用第三方库guppy的hpy来查看, 上面的示例代码同时使用了这两种方式

    Person是默认的new-style class, Person_是带有slots的new-style class. 为二者创建100000个对象, 来查看内存大小.

    直接算Person_的大小总共为3.6M左右, Person的大小为3.2M. 我一直很奇怪这个为什么反而大了呢?

    后来发现貌似__dict__的大小没有被算进去, Person.__dict__的总大小为14M, 这样Person的总大小为17.2M, 而Person_没有__dict__属性, 总大小为3.6M, 由此可以看出, slots可以节省非常多的内存. 

    __dict__一般的实现都是用空间换取时间, 所以本身的内存消耗非常大, 在对象数量越多的时候越明显.

    大家如果在项目中发现python占用很大内存的时候, 可以考虑从这个角度去进行内存优化, 大部分情况是可以取得不错的效果.

    水平有限, 欢迎拍砖!

    参考文献

    1. 官方文档: http://docs.python.org/2.7/reference/datamodel.html?highlight=slots#__slots__
    2. slots的源码分析: http://www.kvmapp.com/program/python/python-slots.html
    3. 节省内存的实例: http://tech.oyster.com/save-ram-with-python-slots/
  • 相关阅读:
    MySQL数据库安装和基本使用
    Android studio JNI技术实现与本地C++链接
    笔记 java中i++ 和 ++i的区别
    LaTeX中添加usepackage{subfigure}一直报错的解决办法,亲测
    Android Studio 出现“Cannot resolve symbol” 解决办法
    记录一个sql
    linux 文件内容查找、去重、统计
    MVC框架的模板缓存
    Linux 命令学习
    笔记 :PHP中的数据结构库SPL
  • 原文地址:https://www.cnblogs.com/icejoywoo/p/3616869.html
Copyright © 2011-2022 走看看