简介
__slots__
允许我们声明并限定类成员,并拒绝类创建__dict__
和__weakref__
属性以节约内存空间。
Python是动态语言,对于普通的类,可以为类实例赋值任何属性,这些属性会存储在__dict__
中:
>>> class Student(object):... pass... >>> Abey = Student()>>> Abey.name = 'Abey'>>> Abey.__dict__ {'name': 'Abey'}
这样的特性带来两个问题:
数据通过字典(Hash)存储所占用的空间较大
如何禁止随意生成类属性
当然,__slots__
就能解决这两个问题。通过__slots__
属性限定类属性的创建:
>>> class Student(object):... __slots__ = ('name', 'age') ... >>> Abey = Student()>>> Abey.name = 'Abey'>>> Abey.gender = 'Female'Traceback (most recent call last): File "<input>", line 1, in <module>AttributeError: 'Student' object has no attribute 'gender'>>> Abey.__dict__Traceback (most recent call last): File "<input>", line 1, in <module>AttributeError: 'Student' object has no attribute '__dict__'
可以看到,在定义了__slots__
变量后,Student类实例已经不能随意创建不在__slots__
定义内的属性gender,同时实例中也不再有__dict__
结构。
用法
继承树
__slots__
在继承中有两种表现:
子类未声明
__slots__
时,不继承父类的__slots__
,即此时子类实例可以随意赋值属性子类声明
__slots__
时,继承父类的__slots__
,即此时子类的__slots__
为其自身+父类的__slots__
以下面的父类为例:
>>> class Student(object):... __slots__ = ('name', 'age')...
创建一个子类不声明__slots__
,该类实例可以创建父类__slots__
限定之外的属性gender:
>>> class SubStudent(Student):... pass... >>> Bob = SubStudent()>>> Bob.gender = 'Male'>>> Bob.__dict__ {'gender': 'Male'}
而创建一个声明__slots__
的子类,该类属性则只能创建父类__slots__
+自身__slots__
限定的属性:
>>> class SubStudent2(Student):... __slots__ = 'gender'... >>> Cathy = SubStudent2()>>> Cathy.gender = 'Female'>>> Cathy.name = 'Cathy'>>> Cathy.teacher = 'Mrs. Wang'Traceback (most recent call last): File "<input>", line 1, in <module>AttributeError: 'SubStudent2' object has no attribute 'teacher'
注意:子类的__slots__
本身已经继承自父类,无需重复声明父类已声明的属性。例如上例,重复声明会多占用内存空间:
>>> class SubStudent3(Student):... __slots__ = ('name', 'age', 'gender')... >>> from sys import getsizeof>>> getsizeof(Student()), getsizeof(SubStudent2()), getsizeof(SubStudent3()) (56, 64, 80)
性能对比
我们为什么要使用__slots__
呢?
更快速地赋值属性
参考Stack Overflow回答中给出的数据:
import timeitclass Foo(object): __slots__ = 'foo',class Bar(object): passslotted = Foo() not_slotted = Bar()def get_set_delete_fn(obj): def get_set_delete(): obj.foo = 'foo' obj.foo del obj.foo return get_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.28468345299916111.2873325658284342
节约内存空间
由于不使用__dict__
存储对象的属性,__slots__
在一些场景下能够节约极大的内存空间。具体数据可以查看参考中的回答链接,不赘述。
作者:严北
链接:https://www.jianshu.com/p/9e17a03d08b1
共同学习,写下你的评论
评论加载中...
作者其他优质文章