Python, __slots__, наследование и переменные класса ==> атрибут - только для чтения
У меня есть большое дерево с сотнями тысяч узлов, и я использую __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
? Я знаю, что мой пример изобретателен и вряд ли будет преднамеренным в реальной программе, но это не делает это поведение менее странным.
Спасибо,
Джонатан
Ответы
Ответ 1
Небольшой пример:
class C(object):
__slots__ = ('x',)
x = 0
C().x = 1
Документация по слотам гласит:
__slots__
реализуются на уровне класса путем создания дескрипторов (Implementing Descriptors) для каждого имени переменной. В результате атрибуты класса не могут использоваться для установки значений по умолчанию для переменных экземпляра, определенных __slots__
; в противном случае атрибут class перезапишет присвоение дескриптора.
Когда используется __slots__
, назначение атрибутов атрибутов слотов должно проходить через дескрипторы, созданные для атрибутов слотов. Затенение дескрипторов в подклассе приводит к тому, что Python не может найти процедуру, необходимую для установки атрибута. Однако Python все еще может видеть, что атрибут существует (поскольку он находит объект, который скрывает дескриптор), поэтому он сообщает, что атрибут доступен только для чтения.
Ответ 2
class NodeBase(object):
__slots__ = ["name"]
def __init__(self, name):
self.name = name
class NodeTypeA(NodeBase):
super().name = "Brian"
__slots__ = ["foo"]
супер()