【问题标题】:Python, __slots__, inheritance, and class variables ==> attribute is read-only bugPython、__slots__、继承和类变量 ==> 属性是只读的 bug
【发布时间】:2011-08-11 02:30:20
【问题描述】:

我有一棵有数十万个节点的大树,我正在使用__slots__ 来减少内存消耗。我刚刚发现了一个非常奇怪的错误并修复了它,但我不明白我看到的行为。

这是一个简化的代码示例:

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 错误?我知道我的示例是人为的,在实际程序中不太可能是故意的,但这并没有使这种行为变得不那么奇怪。

谢谢,
乔纳森

【问题讨论】:

  • NodeTypeA 正在创建一个类变量name,而不是为NodeBase 中定义的实例变量赋值。这是故意的吗?
  • 一开始并不是故意的——我不小心在我的代码中出现了这个问题,导致了我所询问的错误。但是后来我很好奇为什么代码会这样,所以我故意把它放在我的代码示例中。

标签: python slots


【解决方案1】:

一个较小的例子:

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

C().x = 1

documentation on slots 在某一时刻声明:

__slots__ 在类级别通过为每个变量名创建描述符(实现描述符)来实现。因此,类属性不能用于为__slots__定义的实例变量设置默认值;否则,类属性将覆盖描述符分配。

当__slots__在使用时,槽属性的属性分配需要通过为槽属性创建的描述符。隐藏子类中的描述符会导致 Python 无法找到设置属性所需的例程。不过,Python 仍然可以看到属性存在(因为它找到了隐藏描述符的对象),因此它报告该属性是只读的。

【讨论】:

  • 类似的答案也贴在这里:stackoverflow.com/questions/820671/…
  • 好点。我没有意识到它简化了——我认为继承是问题的一部分。显然,正如@Santa 指出的那样,这与上一个问题相同。
  • Python 不会在此处创建只读描述符。发生的情况是实例没有__dict__,而NodeTypeA 中的name = "Brian" 字符串隐藏了为NodeBase 中的name 槽创建的槽描述符。 Python 看到该实例具有 name 属性(因为属性查找找到了 "Brian" 字符串),但该属性不可分配(因为 "Brian" 没有 __set__ 方法并且实例没有有一个__dict__),所以它报告该属性是只读的。
  • 如果你想看代码,PyObject_GenericSetAttr 是涉及到的__setattr__ 实现。
  • 您还可以通过手动检查类型字典来看到不涉及只读描述符。对于NodeTypeA,NodeTypeA.__dict__['name'] 是常规字符串,而不是描述符(对于您的C 类,C.__dict__['x'] 是0)。对于NodeBase,NodeBase.__dict__['name']是一个支持读、写和删除的描述符,你可以通过手动调用__get__、__set__和__delete__看到。
猜你喜欢
  • 2010-10-23
  • 1970-01-01
  • 2021-05-07
  • 1970-01-01
  • 2013-02-11
  • 2013-03-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多