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

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

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

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

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

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

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

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

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

  1. class test_slots(object):
  2. __slots__='x','y'
  3. def printHello(self):
  4. print 'hello!'
  5. class test(object):
  6. def printHello(self):
  7. print 'hello'
  8. print dir(test_slots) #可以看到test_slots类结构里面包含__slots__,x,y
  9. print dir(test)#test类结构里包含__dict__
  10. print '**************************************'
  11. ts=test_slots()
  12. t=test()
  13. print dir(ts) #可以看到ts实例结构里面包含__slots__,x,y,不能任意绑定属性
  14. print dir(t) #t实例结构里包含__dict__,可以任意绑定属性
  15. print '***************************************'
  16. ts.x=11 #只能绑定__slots__名称集合里的属性
  17. t.x=12 #可以任意绑定属性
  18. print ts.x,t.x
  19. ts.y=22 #只能绑定__slots__名称集合里的属性
  20. t.y=23  #可以任意绑定属性
  21. print ts.y,t.y
  22. #ts.z=33 #无法绑定__slots__集合之外的属性(AttributeError: 'test_slots' object has no attribute 'z')
  23. t.z=34 #可以任意绑定属性
  24. 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来标记哪个函数被跟踪。】

下面,我们看一个例子:

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

运行结果,如下图:

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

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

运行结果,如下图:

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

python __slots__ 详解(上篇)的更多相关文章

  1. Python Collections详解

    Python Collections详解 collections模块在内置数据结构(list.tuple.dict.set)的基础上,提供了几个额外的数据结构:ChainMap.Counter.deq ...

  2. Python闭包详解

    Python闭包详解 1 快速预览 以下是一段简单的闭包代码示例: def foo(): m=3 n=5 def bar(): a=4 return m+n+a return bar >> ...

  3. [转] Python Traceback详解

    追莫名其妙的bugs利器-mark- 转自:https://www.jianshu.com/p/a8cb5375171a   Python Traceback详解   刚接触Python的时候,简单的 ...

  4. python 数据类型详解

    python数据类型详解 参考网址:http://www.cnblogs.com/linjiqin/p/3608541.html 目录1.字符串2.布尔类型3.整数4.浮点数5.数字6.列表7.元组8 ...

  5. Python 递归函数 详解

    Python 递归函数 详解   在函数内调用当前函数本身的函数就是递归函数   下面是一个递归函数的实例: 第一次接触递归函数的人,都会被它调用本身而搞得晕头转向,而且看上面的函数调用,得到的结果会 ...

  6. python线程详解

    #线程状态 #线程同步(锁)#多线程的优势在于可以同时运行多个任务,至少感觉起来是这样,但是当线程需要共享数据时,可能存在数据不同步的问题. #threading模块#常用方法:'''threadin ...

  7. python数据类型详解(全面)

    python数据类型详解 目录1.字符串2.布尔类型3.整数4.浮点数5.数字6.列表7.元组8.字典9.日期 1.字符串1.1.如何在Python中使用字符串a.使用单引号(')用单引号括起来表示字 ...

  8. python生成器详解

    1. 生成器 利用迭代器(迭代器详解python迭代器详解),我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成.但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记 ...

  9. 转 python数据类型详解

    python数据类型详解 目录 1.字符串 2.布尔类型 3.整数 4.浮点数 5.数字 6.列表 7.元组 8.字典 9.日期 1.字符串 1.1.如何在Python中使用字符串 a.使用单引号(' ...

随机推荐

  1. Eclipse的SVN插件 Subclipse

    原文:https://www.oschina.net/p/subclipse Subclipse 是一个为 Eclipse IDE 添加 Subversion 支持的项目.支持几乎所有版本的Eclip ...

  2. fuse-dfs挂载hdfs实录

    部署安装了最新稳定版hadoop2.2.0.然后在网上找来fuse-dfs编译教程.可是最后失败了.至今原因未知--,错误描写叙述为:Transport endpoint is not connect ...

  3. start com.android.settings/com.android.settings.SubSettings activity

    1. get class name: adb shell shell@android:/mnt/sdcard/books $ dumpsys window windows >dump.txt g ...

  4. easyUI的treegrid添加节点(append)时间过长,设置等待(wait)遮罩效果

    如题所述,在treegrid中,一次添加多个节点时,时间很长,但easyUI并无显示等待信息,让人以为是陷入了死循环,或者死机了,用户体验很差. 本来,treegrid(或者datagrid)有所谓的 ...

  5. gitlab常见错误解决办法

    1 fail: gitlab-workhorse: runsv not running systemctl start gitlab-runsvdir.service systemctl status ...

  6. 混合使用Delphi和C ++(附下载)

    您想将C ++添加到Delphi应用程序中吗?或者将Delphi代码添加到C ++应用程序中?这是如何做. 您可能不知道的一件事是如何在RAD Studio中集成C ++和Delphi语言.您可以将单 ...

  7. 【IOI2013】【Bzoj3246】Dreaming

    http://www.lydsy.com/JudgeOnline/problem.php?id=3246 中文题面 天地之初,世界尚在遥远的梦想之中. Serpent(水蛇)生活的地方有N个水坑,编号 ...

  8. leetcode 690. Employee Importance——本质上就是tree的DFS和BFS

    You are given a data structure of employee information, which includes the employee's unique id, his ...

  9. [LeetCode] LRU Cache [Forward]

    Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...

  10. 洛谷P1365 WJMZBMR打osu! / Easy——期望DP

    题目:https://www.luogu.org/problemnew/show/P1365 平方和怎样递推? 其实就是 (x+1)^2 = x^2 + 2*x + 1: 所以我们要关注这里的 x — ...