Позвольте мне начать с того, что я понимаю, как слоты и метаклассы работают на Python. Играя с ними, я столкнулся с интересной проблемой. Вот минимальный пример:Можно ли добавить __slots__ в декоратор класса, который уже определяет __slots__?
def decorator(cls):
dct = dict(cls.__dict__)
dct['__slots__'] = ('y',)
return type('NewClass', cls.__bases__, dct)
@decorator
class A(object):
__slots__= ('x',)
def __init__(self):
self.x = 'xx'
A()
В результате получается следующее исключение:
Traceback (most recent call last):
File "p.py", line 12, in <module>
A()
File "p.py", line 10, in __init__
self.x = 'xx'
TypeError: descriptor 'x' for 'A' objects doesn't apply to 'NewClass' object
Теперь я знаю, почему это происходит: дескриптор, созданный для слота x
должен иметь возможность ссылаться на зарезервированное пространство для слот. Только экземпляры класса A или экземпляры подклассов A имеют это зарезервированное пространство, и поэтому только эти экземпляры могут использовать дескриптор x
. В приведенном выше примере метакласс создает новый тип, который является подклассом базовых классов A, но не самого A, поэтому мы получаем исключение. Достаточно просто.
Конечно, в этом простом примере, либо из следующих двух определений decorator
будет работать вокруг проблемы:
def decorator(cls):
dct = dict(cls.__dict__)
dct['__slots__'] = ('y',)
return type('NewClass', (cls,) + cls.__bases__, dct)
def decorator(cls):
class NewClass(cls):
__slots__ = ('y',)
return NewClass
Но эти обходные не совсем он такой же, как оригинал, так как они оба добавляют A в качестве базового класса. Они могут терпеть неудачу в более сложной настройке. Например, если дерево наследования более сложное, вы можете столкнуться с следующим исключением: TypeError: multiple bases have instance lay-out conflict
.
Так что мой очень специфический вопрос:
ли есть способ создать новый класс, через вызов type
, который изменяет атрибут существующего класса __slots__
, но не добавляет существующий класс как базовый класс нового класса?
Edit:
Я знаю, что строгие Метаклассы другую работу вокруг моих примерах выше. Существует множество способов сделать минимальные примеры работы, но мой вопрос заключается в создании класса через new
, который основан на существующем классе, а не на том, как заставить эти примеры работать. Извините за путаницу.
Edit 2:
Обсуждение в комментариях привело меня более конкретный вопрос, чем я первоначально спросил:
Можно создать класс, через вызов type
, который использует слотов и дескрипторов существующего класса, не являясь потомком этого класса?
Если ответ «нет», я был бы признателен за источник, почему нет.
Когда декоратор выполняет класс уже создан. Вы должны определить метакласс для этого. – JBernardo
Вы хотите, чтобы слот 'x' продолжал работать? – user2357112
Да, я хотел бы, чтобы оба 'x' и' y' работали в результирующем классе. – Narcolei