Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python 3.6.5 "Multiple bases have instance lay-out conflict" when multi-inheritance of classes having __slots__

If I run this code, I'v got the subject error message. But why? And how to avoid it getting the C class having its parents slots?

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

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

class C(A, B):
        __slots__ = []
like image 586
xsubira Avatar asked Oct 30 '18 08:10

xsubira


2 Answers

Simply speak, you just cannot do it.

As stated in Documentation,

Multiple inheritance with multiple slotted parent classes can be used, but only one parent is allowed to have attributes created by slots (the other bases must have empty slot layouts) - violations raise TypeError.

The idea behind __slots__ is to reserve specific slots for each attribute in the memory layout of your instances. A and B are trying to reserve the same part of their memory layout for the slot1 and slot2 attributes, and C can't have the same memory reserved for two attributes. It's just not compatible.


Thanks for JCode metioned in comment, the following method is modified to be correct.

But there is always the way, I personally prefer to use a common base contained all required slots if __slots__ is necessary while there is multiple inherited class.

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))

Update The 4 values will be 88, 88, 88, 312. Though __slots__ reserved.

like image 140
MT-FreeHK Avatar answered Sep 19 '22 19:09

MT-FreeHK


It had (in my opinion) a silly workaround. That's why no TypeError is raised when __slots__ is empty, and having an empty __slots__ attribute preserves the "wondered" python behaviour what warns when assigning to an attribute not defined in __slots__.

So, consider the following metaclass:

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)

An then deploy on base classes.

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'

If we execute following code

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'

This produces following output

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'
like image 30
xsubira Avatar answered Sep 19 '22 19:09

xsubira