Python中__slots__属性介绍与基本使用方法
简介
在廖雪峰的python网站上,他是这么说的
python是动态语言,它允许程序在执行过程中动态绑定属性或者方法(使用MethodTpye)。
某个实例在执行过程中绑定的属性跟方法,仅在该实例中有效,其他同类实例是没有的。
可以通过给class绑定属性/方法,来给所有实例绑定属性/方法:
Student.name='' Student.set_score=set_score
而如果使用__slots__,它仅允许动态绑定()里面有的属性
例如,下面这样会报错
classStudent(): __slots__=('name','age') S1=Student() S1.name='Jack'#ok! S1.score=123#error!
但是我觉得很奇怪,仅有这一个作用吗?于是我再查了其他资料,发现这个函数可以很可观地节约内存,下面来一起看看详细的介绍吧。
__slots__允许我们声明并限定类成员,并拒绝类创建__dict__和__weakref__属性以节约内存空间。
Python是动态语言,对于普通的类,可以为类实例赋值任何属性,这些属性会存储在__dict__中:
>>>classStudent(object): ...pass ... >>>Abey=Student() >>>Abey.name='Abey' >>>Abey.__dict__ {'name':'Abey'}
这样的特性带来两个问题:
- 数据通过字典(Hash)存储所占用的空间较大
- 如何禁止随意生成类属性
当然,__slots__就能解决这两个问题。通过__slots__属性限定类属性的创建:
>>>classStudent(object): ...__slots__=('name','age') ... >>>Abey=Student() >>>Abey.name='Abey' >>>Abey.gender='Female' Traceback(mostrecentcalllast): File"",line1,inAttributeError:'Student'objecthasnoattribute'gender' >>>Abey.__dict__ Traceback(mostrecentcalllast): File"",line1,in AttributeError:'Student'objecthasnoattribute'__dict__'
可以看到,在定义了__slots__变量后,Student类实例已经不能随意创建不在__slots__定义内的属性gender,同时实例中也不再有__dict__结构。
用法
继承树
__slots__在继承中有两种表现:
- 子类未声明__slots__时,不继承父类的__slots__,即此时子类实例可以随意赋值属性
- 子类声明__slots__时,继承父类的__slots__,即此时子类的__slots__为其自身+父类的__slots__
以下面的父类为例:
>>>classStudent(object): ...__slots__=('name','age') ...
创建一个子类不声明__slots__,该类实例可以创建父类__slots__限定之外的属性gender:
>>>classSubStudent(Student): ...pass ... >>>Bob=SubStudent() >>>Bob.gender='Male' >>>Bob.__dict__ {'gender':'Male'}
而创建一个声明__slots__的子类,该类属性则只能创建父类__slots__+自身__slots__限定的属性:
>>>classSubStudent2(Student): ...__slots__='gender' ... >>>Cathy=SubStudent2() >>>Cathy.gender='Female' >>>Cathy.name='Cathy' >>>Cathy.teacher='Mrs.Wang' Traceback(mostrecentcalllast): File"",line1,inAttributeError:'SubStudent2'objecthasnoattribute'teacher'
注意:子类的__slots__本身已经继承自父类,无需重复声明父类已声明的属性。例如上例,重复声明会多占用内存空间:
>>>classSubStudent3(Student): ...__slots__=('name','age','gender') ... >>>fromsysimportgetsizeof >>>getsizeof(Student()),getsizeof(SubStudent2()),getsizeof(SubStudent3()) (56,64,80)
性能对比
我们为什么要使用__slots__呢?
更快速地赋值属性
参考StackOverflow回答中给出的数据:
importtimeit classFoo(object):__slots__='foo', classBar(object):pass slotted=Foo() not_slotted=Bar() defget_set_delete_fn(obj): defget_set_delete(): obj.foo='foo' obj.foo delobj.foo returnget_set_delete
得到测试结果为:
>>>min(timeit.repeat(get_set_delete_fn(slotted)))
0.2846834529991611
>>>min(timeit.repeat(get_set_delete_fn(not_slotted)))
0.3664822799983085
可以看到,在相同的环境(Ubuntu)下,slots为Python3.5带来了接近30%的赋值速度提升:
节约内存空间
>>>0.3664822799983085/0.2846834529991611 1.2873325658284342
由于不使用__dict__存储对象的属性,__slots__在一些场景下能够节约极大的内存空间。具体数据可以查看参考中的回答链接,不赘述。
参考
[1]Usageof__slots__?, AaronHall,StackOverflow
推荐阅读
[1]Datamodel,PythonDocument
[2]python__slots__使你的代码更加节省内存,david_bj,51CTO
[3]使用__slots__,廖雪峰
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。