Python 3.6.5 "多个基有实例布局冲突"时,多继承具有__slots__类



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

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

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

如文件中所述,

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

(在我看来(它有一个愚蠢的变通方法。这就是为什么当__slots__为空时不会引发TypeError,并且具有空的__slots__属性可以保留"奇怪"的python行为,当分配给未在__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'

对于使用带槽类的多重继承,一个实用的选项是只有一个父类具有非空槽。然后,剩下的类充当具有已定义(但为空(槽的"mixin"。然后,在子类中,根据需要简单地定义最后的槽。

如前所述,当所有父对象都定义非空槽时,多重继承是有问题的。

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

最新更新