【问题标题】:Python 3.6.5 "Multiple bases have instance lay-out conflict" when multi-inheritance of classes having __slots__当具有 __slots__ 的类的多重继承时,Python 3.6.5“多个基础存在实例布局冲突”
【发布时间】:2019-04-03 06:38:24
【问题描述】:

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

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

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

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

【问题讨论】:

标签: python multiple-inheritance slots


【解决方案1】:

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

Documentation中所述,

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

__slots__ 背后的想法是为实例的内存布局中的每个属性保留特定的 slotsAB 试图为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__ 保留。

【讨论】:

  • 你说的非常对。我尝试使用元类,但绝对没有出路。非常感谢。
  • 请@MatrixTai,你能帮我考虑一下我自己答案的利弊吗? (在下面)
  • @MatrixTai 你在AB 中没有__slots__,所以你根本没有使用这个功能。 C().x = 100 不会失败。
  • @MatrixTai 你不明白。 __slots__ 不会被继承。问题是,如果C().x = 100 未在任何__slots__ 中列出时x 没有失败,则意味着该功能不起作用。您在B 类层次结构中错过了一个__slots__,而__dict__B 的子类中再次可用。只需在解释器中运行您的代码并分配C().x
  • @MatrixTai 只需将空的__slots__ 添加到AB。我正在谈论的事情是在您链接的文档中:“但是,子子类将获得__dict____weakref__,除非它们还定义了__slots__(它应该只包含任何附加插槽的名称)。 "我知道您可能会错过它,因为该文档写得不好 IMO。
【解决方案2】:

它有(在我看来)一个愚蠢的解决方法。 这就是为什么当__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'

【讨论】:

  • 请@MatrixTai,你能帮我考虑一下我自己的答案的利弊吗?
  • 不,你不能这样做,A类和B类在这个意义上不会有任何插槽。您不能在课程构建后分配__slots__。只有当您从不单独使用 A、B 类时,您才可以这样做。
【解决方案3】:

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

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

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

【讨论】:

    猜你喜欢
    • 2018-06-16
    • 2011-11-20
    • 1970-01-01
    • 2011-07-29
    • 2015-04-27
    • 2012-11-14
    • 2021-02-08
    • 1970-01-01
    • 2021-12-15
    相关资源
    最近更新 更多