Python, __slots__,继承与类变量 ==> 属性是只读错误

19 投票
1 回答
7918 浏览
提问于 2025-04-16 16:15

我有一棵很大的树,里面有成千上万个节点,我使用 __slots__ 来减少内存消耗。最近我发现了一个很奇怪的 bug,并且修复了它,但我不太明白我看到的行为。

这里有一段简化的代码示例:

class NodeBase(object):
    __slots__ = ["name"]
    def __init__(self, name):
        self.name = name

class NodeTypeA(NodeBase):
    name = "Brian"
    __slots__ = ["foo"]

然后我执行了以下代码:

>>> node = NodeTypeA("Monty")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in __init__
AttributeError: 'NodeTypeA' object attribute 'name' is read-only

如果 NodeTypeA.name 没有定义,就不会出现错误(顺便说一下:那个属性是错误加上去的,根本没有必要存在)。如果 NodeTypeA.__slots__ 从来没有定义过,它就会有一个 __dict__,也不会出错。

我不明白的是:为什么在父类中存在一个类变量会影响在子类的槽中设置实例变量?

有没有人能解释一下为什么这种组合会导致 object attribute is read-only 的错误?我知道我的例子有点牵强,在真实程序中不太可能故意出现,但这并不妨碍这个行为看起来很奇怪。

谢谢,
Jonathan

相关问题:

1 个回答

26

这里有一个简单的例子:

class C(object):
    __slots__ = ('x',)
    x = 0

C().x = 1

关于 __slots__ 的文档中提到:

__slots__ 是在类的层面上通过为每个变量名创建描述符来实现的(实现描述符)。因此,类属性不能用来为由 __slots__ 定义的实例变量设置默认值;否则,类属性会覆盖描述符的赋值。

当使用 __slots__ 时,给槽属性赋值需要通过为这些槽属性创建的描述符来进行。如果在子类中遮蔽了这些描述符,Python 就无法找到设置属性所需的例程。不过,Python 仍然能看到这个属性的存在(因为它找到了遮蔽描述符的对象),所以它会报告这个属性是只读的。

撰写回答