当一个类需要创建大量实例时,可以通过
__slots__
声明实例所需要的属性,本文介绍__slots__
相关内容。
简介 {#简介}
Python 中一切皆为对象,类也是如此,类中的变量具有强大的动态灵活性,这依赖于变量实现了类似字典管理的机制。但当我们需要限制实例的属性时该如何实现?
限制变量范围 {#限制变量范围}
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__
变量,来限制该class实例能添加的属性:
由于'score'没有被放到__slots__中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。
提升成员访问性能 {#提升成员访问性能}
__slots__
变量设置了类访问成员的活动范围,舍弃了原始Python类的灵活性,但是收益是类成员访问效率的提升。
python文档中这样介绍它
3.3.2.4. *slots*
slots 允许我们显式地声明数据成员(例如特征属性)并禁止创建 dict 和 weakref (除非是在 slots 中显式地声明或是在父类中可用。)
相比使用 dict 此方式可以显著地节省空间。 属性查找速度也可得到显著的提升。
python的动态性一部分源自于__dict__
,属性都保存在__dict__
的一个字典中,我们可以随时向这个字典添加新内容,这是MonkeyPatch 的能力。
而当我们显示的声明了__slots__
,python将不会给这个类创建__dict__
和 __weakref__
注:
__weakref__
是软引用,类似于指针,不会增加引用计数器
继承问题 {#继承问题}
但是__slots__
遇到继承时,就会出现很多问题。准确的说,__slots__
不会被继承。当我们用一个不带有__slots__
的子类,继承带有__slots__
的父类时。子类还是会生成__dict__
和 __weakref__
。
为解决__slots__
无法继承的问题可以采用 metaclass 的能力。
- 这个Metaclass 在被
单继承
时执行了如下逻辑:
graph TD
A{父类是否有__slots__}
B{子类是否有__slots__}
C[跳过]
D[跳过]
E[设置__slots__为空]
A -- 否 -->C
A -- 是 --> B
B -- 否 --> D
B -- 是 --> E
- 继承结果
| | 父类有 __slot__
| 父类无 __slot__
|
|----------------|--------------------------------------|----------------------------|
| 子类有 __slot__
| 子类的的实际范围为父子__slots__
的并集 | 子类的的实际范围为子类本身的__slots__
|
| 子类无 __slot__
| 子类__slots__= ()
,无法创建新成员,完全遵循父类的规则 | 子类没有设置 __slots__
|
- 可以看到当父类有
__slots__
而子类没有时(也就是我们期望继承时),子类被约束在父类的规则之下,无法创建新成员,其他情况不受影响,也就达到了我们需要继承父类__slots__
的目的
约束范围 {#约束范围}
需要注意一点,__slots__
是类对实例属性的约束,而类对象无法通过该属性,约束自己。即为类对象添加新属性,仍然是被允许的。
参考资料 {#参考资料}
- https://www.cnblogs.com/zmc940317/p/10284560.html
- https://zhuanlan.zhihu.com/p/64195966
- https://www.jianshu.com/p/381c55367c3f
文章链接:
https://www.zywvvd.com/notes/coding/python/python-slots/python-slots/