Dust8 的博客

读书百遍其义自见

0%

python __slots__

起因

最近写 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__ 不适用于从“可变长度”内置类型(如 intbytestuple)派生的类。
  • 任何非字符串可迭代可以分配给 __slots__。 也可以使用映射, 然而,将来,可以对与每个键相对应的值分配特殊的含义。
  • __class__ 赋值只有当两个类都有相同的 __slots__是才有效。

总结

__slots__ 有2大用途: 一个是节省空间(大量实例时才明显),另一个是加快操作实例的速度(官方没说)。至于本文开头说的问题,暂时还没想明白,以后补上。
好吧,最后发现是 __init__ 写成 __int__ 了,我也是醉了。