繁体   English   中英

Python如何通过多重继承传递__init__参数

[英]How does Python pass __init__ parameters with multiple inheritance

我有以下代码,显示了经典的菱形图案:

class A:
    def __init__( self, x ):
        print( "A:" + x )


class B( A ):
    def __init__( self, x ):
        print( "B:" + x )
        super().__init__( "b" )


class C( A ):
    def __init__( self, x ):
        print( "C:" + x )
        super().__init__( "c" )


class D( B, C ):
    def __init__( self ):
        super().__init__( "d" )


d = D()

输出为:

B:d
C:b
A:c
  • B:d是有道理的,因为D源自B
  • A:c我几乎得到了,尽管我同样可以看到A:b
  • 但是, C:b位没有意义: C不是从B派生的。

有人可以解释吗?

问题如这个不幸的是没有提到的参数。

Python使用C3线性化算法来建立方法的解析顺序,这与super委托的顺序相同。

基本上,该算法会为每个包含该类的类及其继承的每个类以及所涉及的类所继承的所有类保留列表。 然后,它通过逐个获取未被任何未经检查的类继承的类来构造类的排序,直到到达根object为止。 下面,为了简洁起见,我将O用作object

L(O) = [O]

L(A) = [A] + merge(L(O), [O]) = [A, O]

L(B) = [B] + merge(L(A), [A]) = [B] + merge([A, O], [A]) = [B, A] + merge([O]) 
     = [B, A, O]

L(C) = [C] + merge(L(A), [A]) = [C] + merge([A, O], [A]) = [C, A] + merge([O]) 
     = [C, A, O]

L(D) = [D] + merge(L(B), L(C), [B, C]) = [D] + merge([B, A, O], [C, A, O], [B, C])
     = [D, B] + merge([A, O], [C, A, O], [C]) = [D, B, C] + merge([A, O], [A, O])
     = [D, B, C, A, O]

Python中的类是动态组成的-包括继承。

C:b输出并不表示B神奇地继承自C 如果您实例化BC ,那么任何人都不知道对方。

>>> B('root')
B:root
A:b

但是, D 确实知道BC

class D(B,C):
    ...

有很多可用的技术 但是,工作原理基本上分为两部分:

  1. 直接基类按照出现的顺序进行解析。
    • BC之前。
  2. 递归基类被解析为不重复。
    • BC基类必须都遵循。

对于类D ,这意味着基类解析为B->C->A C已经潜入BA之间-但仅适用于D级,而不是B级。


请注意,实际上涉及另一个类:默认情况下,所有类都从object派生。

>>> D.__mro__
(__main__.D, __main__.B, __main__.C, __main__.A, object)

您已经写了A知道没有任何基础可以接受其参数。 但是, BC都不能假定这一点。 他们都期望从A对象派生。 子类化确实意味着BC也是有效的A对象,但是!

B C都在B C之前是有效的,因为这两个都是A子类。 B->C->A->object不会破坏B期望其超类为A类型。

对于所有其他组合,最后以C开头(无效)或object开头(无效)。 那排除了深度优先分辨率B->A->object->C并复制了B->A->object->C->A->object


此方法解析顺序对启用mixins很有用:依赖其他类的类定义如何解析方法。

有一个很好的示例 ,说明字典访问记录器如何接受dictOrderedDict

# basic Logger working on ``dict``
class LoggingDict(dict):
    def __setitem__(self, key, value):
        logging.info('Settingto %r' % (key, value))
        super().__setitem__(key, value)

# mixin of different ``dict`` subclass
class LoggingOD(LoggingDict, collections.OrderedDict):
    pass

您始终可以检查任何类应具有的方法解析顺序:

 >>> D.mro()
 [__main__.D, __main__.B, __main__.C, __main__.A, object]

如您所见,如果每个人都在做正确的事情(即调用super),那么MRO将是第一父级,第二父级,第一父级的父级,依此类推...

您可以先考虑深度,然后从左至右查找顺序,尽管自python 2.3起算法发生了变化,但结果通常是相同的。

在这种情况下,B和C具有相同的父A,而A不调用super

暂无
暂无

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

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