Атрибут Python, __slots__, наследование и переменные класса ==> доступны только для чтения - PullRequest
15 голосов
/ 22 апреля 2011

У меня большое дерево с сотнями тысяч узлов, и я использую __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? Я знаю, что мой пример надуманный и вряд ли будет преднамеренным в реальной программе, но это не делает это поведение менее странным.

Спасибо
Jonathan

1 Ответ

18 голосов
/ 22 апреля 2011

Меньший пример:

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

C().x = 1

Документация о слотах указывает на одну точку:

__slots__ реализуются на уровне класса путем создания дескрипторов (Implementing Descriptors) для каждого имени переменной. В результате атрибуты класса не могут использоваться для установки значений по умолчанию для переменных экземпляра, определенных __slots__; в противном случае атрибут class перезапишет присвоение дескриптора.

Когда используется __slots__, присваивание атрибутов атрибутам слотов должно проходить через дескрипторы, созданные для атрибутов слотов. Затенение дескрипторов в подклассе приводит к тому, что Python не может найти процедуру, необходимую для установки атрибута. Однако Python по-прежнему может видеть, что атрибут существует (поскольку он находит объект, который скрывает дескриптор), поэтому он сообщает, что атрибут доступен только для чтения.

...