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

时间:2018-10-30 08:59:25

标签: python multiple-inheritance slots

如果我运行此代码,则会收到主题错误消息。但为什么?以及如何避免让C类具有其父级插槽?

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

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

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

3 个答案:

答案 0 :(得分:4)

简单地说,你就是做不到。

Documentation中所述,

  

可以使用具有多个带槽父级类的多重继承,但只允许一个父级具有由插槽创建的属性(其他基类必须具有空插槽布局)-违反会引发TypeError。

__slots__背后的想法是为实例的内存布局中的每个属性保留特定的插槽AB试图为slot1slot2属性保留其内存布局的相同部分,并且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__保留。

答案 1 :(得分:0)

(我认为)它有一个愚蠢的解决方法。 这就是为什么在TypeError为空时不引发__slots__的原因,而拥有空的__slots__则保留了“奇怪”的python行为,该行为会在分配给{{1}中未定义的属性时发出警告}。

因此,请考虑以下元类

__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'

答案 2 :(得分:0)

对于将多继承与带槽类一起使用,一个实际的选择是只有一个父类具有非空槽。然后,其余类用作具有定义(但为空)插槽的“混合”。然后,在子类中,只需根据需要定义最后的广告位。

正如已经显示的那样,当所有父级都定义非空槽时,多重继承是有问题的。

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

此处建议的方法使C成为定义了空插槽的“ mixin”类。然后,使用多重继承的子类可以简单地将插槽定义为所需的任何位置。

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