Python 3.6.5 «Несколько баз имеют конфликт макетов экземпляров», когда множественное наследование классов, имеющих __slots__ - PullRequest
0 голосов
/ 30 октября 2018

Если я запускаю этот код, я получаю сообщение об ошибке субъекта. Но почему? И как избежать того, чтобы класс C имел слоты для родителей?

class A():
        __slots__ = ['slot1']

class B():
        __slots__ = ['slot2']

class C(A, B):
        __slots__ = []

Ответы [ 2 ]

0 голосов
/ 30 октября 2018

У него был (на мой взгляд) глупый обходной путь. Вот почему TypeError не вызывается, когда __slots__ пусто, а наличие пустого атрибута __slots__ сохраняет «удивленное» поведение питона, которое выдает предупреждение при назначении атрибута, не определенного в __slots__.

Итак, рассмотрим следующий метакласс :

class SlotBase(type):
    def __new__(cls,name,bases,dctn):
        if ('_slots_' in dctn) and not ('__slots__' in dctn):
            dctn['__slots__'] = []
        elif '__slots__' in dctn:
            for base in bases:
                if hasattr(base,'_slots_'):
                    dctn['__slots__'] += getattr(base,'_slots_')
        return super().__new__(cls,name,bases,dctn)

Затем разверните на базовых классах.

class A(metaclass=SlotBase):

    _slots_=['slot1'] #fake __slots__ attribute

    classPropertyA = 'Some silly value'

    def functA(self):
        print('I\'m functA')

class B(metaclass=SlotBase):

    _slots_=['slot2'] #fake __slots__ attribute

    classPropertyB = 'Some other silly value'

    def functB(self):
        print('I\'m functB')

class C(A,B):
    __slots__ = []

    classPropertyC = 'Just another silly value'

Если мы выполним следующий код

c=C()
c.classPropertyC
c.classPropertyA
c.functA()
c.functB()
c.slot1='Slot exists then assignment is accepted'
c.slot3='Slot does not exists then assignment couldn\'t be accepted'

Это дает следующий вывод

Just another silly value
Some silly value
I'm functA
I'm functB
Traceback (most recent call last):
  File "/tmp/slots.py", line 41, in <module>
    c.slot3='Slot does not exists then assignment couldn\'t be accepted'
AttributeError: 'C' object has no attribute 'slot3'
0 голосов
/ 30 октября 2018

Проще говоря, вы просто не можете этого сделать.

Как указано в Документация ,

Может использоваться множественное наследование с несколькими родительскими классами с выделенными слотами, но только один родитель может иметь атрибуты, созданные с помощью слотов (другие базы должны иметь пустые макеты слотов) - нарушения приводят к возникновению ошибки TypeError.

Идея __slots__ состоит в том, чтобы зарезервировать определенные слоты для каждого атрибута в макете памяти ваших экземпляров. A и B пытаются зарезервировать одну и ту же часть своего макета памяти для атрибутов slot1 и slot2, а C не может зарезервировать одну и ту же память для двух атрибутов. Это просто не совместимо.


Спасибо за JCode, упомянутый в комментарии, следующий метод изменен для корректности.

Но всегда есть способ, я лично предпочитаю использовать общую базу, содержащую все необходимые слоты, если __slots__ необходим, когда существует несколько унаследованных классов.

import pympler.asizeof
class base():
    __slots__ = ['a','b']

class A(base):
    __slots__ = []

class B(base):
    __slots__ = []

class C(A,B):
    __slots__ = []

class D():
    pass

#Update
bb = base()
bb.a = 100
bb.b = 100
print(pympler.asizeof.asizeof(bb))
a = A()
a.a = 100
a.b = 100
print(pympler.asizeof.asizeof(a))
c = C()
c.a = 100
c.b = 100
print(pympler.asizeof.asizeof(c))
d = D()
d.a = 100
d.b = 100
print(pympler.asizeof.asizeof(d))

Обновление 4 значения будут 88, 88, 88, 312. Хотя __slots__ зарезервировано.

...