• python __slots__ 详解(上篇)


    转自:http://blog.csdn.net/sxingming/article/details/52892640

    python中的new-style class要求继承Python中的一个内建类型,一般继承object,也可以继承list或者dict等其他的内建类型。
    在python新式类中,可以定义一个变量__slots__,它的作用是阻止在实例化类时为实例分配dict,

    默认情况下每个类都会有一个dict,通过__dict__访问,这个dict维护了这个实例的所有属性,举例如下:

    [python] view plain copy
     
    1. class base(object):  
    2.     var=#类变量  
    3.     def __init__(self):  
    4.         pass  
    5.   
    6. b=base()  
    7. print b.__dict__  
    8. b.x=#添加实例变量  
    9. print b.__dict__  

    运行结果:
    { }
    {'x': 2}
    可见:实例的dict只保持实例的变量,对于类的属性是不保存的,类的属性包括变量和函数。
    由于每次实例化一个类都要分配一个新的dict,因此存在空间的浪费,因此有了__slots__。
    __slots__是一个元组,包括了当前能访问到的属性。
    当定义了slots后,slots中定义的变量变成了类的描述符,相当于java,c++中的成员变量声明,
    类的实例只能拥有slots中定义的变量,不能再增加新的变量。注意:定义了slots后,就不再有dict。如下:

    [python] view plain copy
     
    1. class base(object):  
    2.     __slots__=('x')  
    3.     var=8  
    4.     def __init__(self):  
    5.         pass  
    6.   
    7. b=base()  
    8. b.x=88 #添加实例变量  
    9. print b.x  
    10. #b.y=99 #无法添加slots之外的变量 (AttributeError: 'base' object has no attribute 'y')  
    11. #print b.__dict__ #定义了__slots__后,就不再有__dict__ (AttributeError: 'base' object has no attribute '__dict__')  

    运行结果:
    88
    如果类变量与slots中的变量同名,则该变量被设置为readonly!!!如下:

    [python] view plain copy
     
    1. class base(object):  
    2.     __slots__=('y')  
    3.     y=22 # y是类变量,y与__slots__中的变量同名  
    4.     var=11  
    5.     def __init__(self):  
    6.         pass  
    7.       
    8. b=base()  
    9. print b.y  
    10. print base.y  
    11. #b.y=66 #AttributeError: 'base' object attribute 'y' is read-only  

    运行结果:
    22
    22
    Python是一门动态语言,可以在运行过程中,修改实例的属性和增删方法。一般,任何类的实例包含一个字典__dict__,
    Python通过这个字典可以将任意属性绑定到实例上。有时候我们只想使用固定的属性,而不想任意绑定属性,
    这时候我们可以定义一个属性名称集合,只有在这个集合里的名称才可以绑定。__slots__就是完成这个功能的。

    [python] view plain copy
     
    1. class test_slots(object):  
    2.     __slots__='x','y'  
    3.     def printHello(self):  
    4.         print 'hello!'  
    5.   
    6. class test(object):  
    7.     def printHello(self):  
    8.         print 'hello'  
    9.   
    10. print dir(test_slots) #可以看到test_slots类结构里面包含__slots__,x,y  
    11. print dir(test)#test类结构里包含__dict__  
    12. print '**************************************'  
    13. ts=test_slots()  
    14. t=test()  
    15. print dir(ts) #可以看到ts实例结构里面包含__slots__,x,y,不能任意绑定属性  
    16. print dir(t) #t实例结构里包含__dict__,可以任意绑定属性  
    17. print '***************************************'  
    18. ts.x=11 #只能绑定__slots__名称集合里的属性  
    19. t.x=12 #可以任意绑定属性  
    20. print ts.x,t.x  
    21. ts.y=22 #只能绑定__slots__名称集合里的属性  
    22. t.y=23  #可以任意绑定属性  
    23. print ts.y,t.y  
    24. #ts.z=33 #无法绑定__slots__集合之外的属性(AttributeError: 'test_slots' object has no attribute 'z')  
    25. t.z=34 #可以任意绑定属性  
    26. print t.z   

    运行结果:
    ['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'printHello', 'x', 'y']
    ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'printHello']
    **************************************
    ['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'printHello', 'x', 'y']
    ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'printHello']
    ***************************************
    11 12
    22 23
    34

    正如上面所说的,默认情况下,Python的新式类和经典类的实例都有一个dict来存储实例的属性。这在一般情况下还不错,而且非常灵活,
    乃至在程序中可以随意设置新的属性。但是,对一些在”编译”前就知道有几个固定属性的小class来说,这个dict就有点浪费内存了。
    当需要创建大量实例的时候,这个问题变得尤为突出。一种解决方法是在新式类中定义一个__slots__属性。
    __slots__声明中包含若干实例变量,并为每个实例预留恰好足够的空间来保存每个变量;这样Python就不会再使用dict,从而节省空间。

    【使用memory_profiler模块,memory_profiler模块是在逐行的基础上,测量代码的内存使用率。尽管如此,它可能使得你的代码运行的更慢。使用装饰器@profile来标记哪个函数被跟踪。】

    下面,我们看一个例子:

    [python] view plain copy
     
    1. from  memory_profiler import profile  
    2. class A(object): #没有定义__slots__属性  
    3.     def __init__(self,x):  
    4.         self.x=x  
    5.  
    6. @profile  
    7. def main():  
    8.     f=[A(523825) for i in range(100000)]  
    9.   
    10. if __name__=='__main__':  
    11.     main()  

    运行结果,如下图:

    第2列表示该行执行后Python解释器的内存使用情况,第3列表示该行代码执行前后的内存变化。
    在没有定义__slots__属性的情况下,该代码共使用了20.8MiB内存。
    从结果可以看出,内存使用是以MiB为单位衡量的,表示的mebibyte(1MiB = 1.05MB)

    [python] view plain copy
     
    1. from  memory_profiler import profile  
    2. class A(object):#定义了__slots__属性  
    3.     __slots__=('x')  
    4.     def __init__(self,x):  
    5.         self.x=x  
    6.  
    7. @profile  
    8. def main():  
    9.     f=[A(523825) for i in range(100000)]  
    10.   
    11. if __name__=='__main__':  
    12.     main()  

    运行结果,如下图:

    可以看到,在定义了__slots__属性的情况下,该代码共使用了6.1MiB内存,比上面的20.8MiB节省了很多内存!
    综上所述,在确定了类的属性固定的情况下,可以使用__slots__来优化内存。
    提醒:不要贸然进行这个优化,把它用在所有地方。这种做法不利于代码维护,而且只有生成数以千计的实例的时候才会有明显效果。

  • 相关阅读:
    用户登陆显示cpu、负载、内存信息
    递归算法总结
    Java算法之递归打破及在真实项目中的使用实例
    史上最简单,一步集成侧滑(删除)菜单,高仿QQ、IOS。
    仿饿了么购物车下单效果
    一起来写个酷炫的水波纹浪啊浪界面
    一行实现QQ群组头像,微信群组,圆角等效果. 并支持url直接加载图片
    使用 CoordinatorLayout 实现复杂联动效果
    这交互炸了(三) :不看后悔!你一定没见过这样的闪屏
    这交互炸了(二):爱范儿是如何让详情页缩小为横向列表的
  • 原文地址:https://www.cnblogs.com/eternal1025/p/8547199.html
Copyright © 2020-2023  润新知