简体   繁体   English

当具有 __slots__ 类的多重继承时,Python 3.6.5“多个基类具有实例布局冲突”

[英]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?以及如何避免它让C类拥有其父插槽?

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

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

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

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.可以使用具有多个槽父类的多重继承,但只允许一个父类具有由槽创建的属性(其他基类必须具有空槽布局) - 违规会引发 TypeError。

The idea behind __slots__ is to reserve specific slots for each attribute in the memory layout of your instances. __slots__背后的想法是为实例的内存布局中的每个属性保留特定的插槽 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. AB试图为slot1slot2属性保留其内存布局的相同部分,而C不能为两个属性保留相同的内存。 It's just not compatible.它只是不兼容。


Thanks for JCode metioned in comment, the following method is modified to be correct.感谢评论中提到的 JCode,修改以下方法为正确。

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.但是总有办法,如果__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))

Update The 4 values will be 88, 88, 88, 312. Though __slots__ reserved.更新4 个值将是__slots__ 。虽然__slots__保留。

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__ .这就是为什么当__slots__为空时不会TypeError的原因,并且具有空的__slots__属性保留了“奇怪的”python 行为,当分配给__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'

For using multiple-inheritance with slotted classes, a practical option is have only one parent class have non-empty slots.对于带槽的类使用多重继承,一种实用的选择是只有一个父类具有非空槽。 The remaining classes then serve as "mixins" with defined (but empty) slots.其余的类然后用作具有定义(但为空)插槽的“混合”。 Then, in the child class, simply define the final slots as needed.然后,在子类中,根据需要简单地定义最终插槽。

As already shown, multiple inheritance when all parents define non-empty slots is problematic.如前所述,当所有父项都定义非空槽时,多重继承是有问题的。

>>> class B: __slots__ = ('a', 'b')
... 
>>> class C: __slots__ = ('a', 'b')
... 
>>> class D(C, B): __slots__ = ('a', 'b')
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: multiple bases have instance lay-out conflict

>>> class D(C, B): __slots__ = ('a', 'b', 'c')
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: multiple bases have instance lay-out conflict

The approach suggested here makes C into a "mixin" class that defines empty slots.这里建议的方法使C成为定义空槽的“mixin”类。 Then the child class, using multiple inheritance, can simply define the slots to be whatever is needed.然后子类使用多重继承,可以简单地将插槽定义为所需的任何内容。

>>> class B: __slots__ = ('a', 'b')
... 
>>> class C: __slots__ = ()
... 
>>> class D(C, B): __slots__ = ('a', 'b')
... 
>>> class D(C, B): __slots__ = ('a', 'b', 'c')
... 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM