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

做~自己de王妃 提交于 2019-12-05 04:46:56
MT-FreeHongKong

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.

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'
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!