Python __slots__ 详解:内存优化与性能提升指南
本文深入解析 Python 中 __slots__ 属性的作用与用法。通过限制类动态添加属性,__slots__ 能有效消除 __dict__ 带来的内存开销,并显著提升属性赋值与访问速度。文章涵盖 __slots__ 在继承中的行为表现、性能对比数据及具体代码示例,帮助开发者在大型应用或内存敏感场景中优化类设计。
简介
参考廖雪峰 Python 教程中的相关描述:
Python 属于动态语言,允许程序在运行期间动态绑定属性或方法(通过 MethodType)。
在实例执行过程中动态绑定的属性和方法,仅对该特定实例生效,其他同类实例无法直接访问这些动态绑定的内容。
若需为所有实例统一绑定属性或方法,可直接对 class 进行绑定操作:
Student.name = '' Student.set_score = set_score
若使用 ,则仅允许动态绑定 中声明的属性。
例如,尝试绑定未在 中声明的属性会引发报错:
class Student():
__slots__ = ('name', 'age')
S1 = Student()
S1.name = 'Jack' # ok!
S1.score = 123 # error!
起初这可能看似仅有限制作用,但查阅其他资料后发现,该机制能显著节约内存,下文将详细介绍。
允许声明并限定类成员,同时阻止类创建 和 属性,从而节省内存空间。
Python 作为动态语言,普通类的实例可赋值任意属性,这些属性默认存储在 中:
>>> class Student(object):
... pass
...
>>> Abey = Student()
>>> Abey.name = 'Abey'
>>> Abey.__dict__
{'name': 'Abey'}
这种动态特性主要引发两个问题:
- 数据通过字典(Hash)存储占用空间较大
- 难以禁止随意生成类属性
正是解决上述两个问题的方案。通过 属性限定类属性的创建范围:
>>> 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__'
定义 变量后, 类实例无法随意创建超出 定义范围的属性(如 gender),且实例中不再存在 结构。
用法
继承树
__slots__ 在继承关系中有两种表现:
- 子类未声明 时,不继承父类的 ,此时子类实例可随意赋值属性。
- 子类声明 时,继承父类的 ,此时子类的 为其自身声明加上父类的 。
以下面的父类为例:
>>> class Student(object):
... __slots__ = ('name', 'age')
...
创建一个未声明 的子类,该实例可创建父类 限定之外的属性 gender:
>>> class SubStudent(Student):
... pass
...
>>> Bob = SubStudent()
>>> Bob.gender = 'Male'
>>> Bob.__dict__
{'gender': 'Male'}
而创建一个声明了 的子类,其属性只能创建父类 加上自身 限定的属性:
>>> 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'
注意:子类的 本身已继承自父类,无需重复声明父类已存在的属性。例如上例中,若重复声明会额外占用内存空间:
>>> class SubStudent3(Student):
... __slots__ = ('name', 'age', 'gender')
...
>>> from sys import getsizeof
>>> getsizeof(Student()), getsizeof(SubStudent2()), getsizeof(SubStudent3())
(56, 64, 80)
性能对比
我们为何要使用 ?
更快速地赋值属性
参考 Stack Overflow 回答中提供的数据:
import timeit class Foo(object): __slots__ = 'foo', class Bar(object): pass slotted = 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)下, 为 Python3.5 带来了接近 30% 的赋值速度提升:
节约内存空间
>>> 0.3664822799983085 / 0.2846834529991611 1.2873325658284342
由于不使用 存储对象属性, 在特定场景下能节约大量内存空间。具体数据可参考回答链接,此处不再赘述。
参考
[1] Usage of __slots__? , Aaron Hall, Stack Overflow
推荐阅读
[1] Data model , Python Document
[2] python __slots__ 使你的代码更加节省内存 , david_bj, 51CTO
[3] 使用__slots__ , 廖雪峰
总结
以上就是本文的全部内容,希望对大家的学习或工作具有参考价值。如有疑问欢迎留言交流,感谢支持。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









