起因
最近写 scrapy
时发现,继承 spider
时重写 __init__
后设置属性,该属性无法访问,会引起 AttributeError
。 以前也在一些 orm
包里重写__init__
时出现该问题。百度+谷歌后还是无果,也许我用的都是假的。翻翻源代码,发现 __slots__
比较可疑。
官方文档翻译
slots
默认情况下,类的实例有一个储存属性的字典。该字典会消耗那些只有少量实例变量的对象的空间。
当创建大量实例时,空间消耗可能变得非常严重。
可以通过在类的定义中,定义 __slots__
来覆盖默认值。__slots__
声明一个实例变量的序列,
并且为每个实例保留足够的空间来保存每个变量的值。因为 __dict__
不会为每一个实例创建,
所以可以节省空间。
object.slots
这个类变量声明是由实例变量名字组成的字符串,可迭代对象,或者字符串序列。__slots__
为已声明的变量预留空间,并阻止为每个实例自动创建 __dict__
和 __weakref__
。
使用 slots 时的注意事项
- 当从没有
__slots__
的类继承时,该类的__dict__
属性将始终可访问,因此子类中的__slots__
定义是无意义的。 - 如果没有
__dict__
变量,则不能为实例指定__slots__
定义中未列出的新变量。 尝试分配到未列出的变量名称会引发AttributeError
。 如果需要动态分配新变量,则在__slots__
声明中的字符串序列中添加__dict__
。 - 如果每个实例没有
__weakref__
变量,定义__slots__
的类就不支持对其实例的弱引用。 如果需要弱引用支持,请在__slots__
声明中的字符串序列中添加__weakref__
。 __slots__
在类级别通过为每个变量名创建描述符(实现描述符)来实现。 因此,类属性不能用于为__slots__
定义的实例变量设置默认值, 否则,类属性将覆盖描述符。__slots__
声明的操作仅限于定义它的类。 因此,子类将有一个__dict__
,除非子类还定义__slots__
(其中只能包含任何其他slots
的名字)。- 如果一个类定义了一个在基类中定义的
slots
,那么由基类slots
定义的实例变量是不可访问的(除了直接从基类检索它的描述符)。 这使得程序的含义未定义。 将来,可能会添加一个检查来防止这种情况。 - 非空
__slots__
不适用于从“可变长度”内置类型(如int
,bytes
和tuple
)派生的类。 - 任何非字符串可迭代可以分配给
__slots__
。 也可以使用映射, 然而,将来,可以对与每个键相对应的值分配特殊的含义。 __class__
赋值只有当两个类都有相同的__slots__
是才有效。
总结
__slots__
有2大用途: 一个是节省空间(大量实例时才明显),另一个是加快操作实例的速度(官方没说)。至于本文开头说的问题,暂时还没想明白,以后补上。
好吧,最后发现是 __init__
写成 __int__
了,我也是醉了。