Python 如何传递具有多重继承__init__参数



我有这个代码,显示一个经典的菱形图案:

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。 下面,为了简洁起见,我使用Oobject

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. 直接基类按其显示顺序进行解析。
    • B先于C.
  2. 递归基类解析为不重复。
    • 同时包含BC的基类必须同时遵循两者。

对于类D,这意味着基类解析为B->C->AC潜入BA之间——但仅限于D班,而不是B班。


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

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

您已经编写了A知道没有基础来获取其参数。但是,BC都不能假设这一点。它们都希望从A对象派生。不过,子类化确实意味着BCA对象也是有效的!

BCBC之前都是有效的,因为两者是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]

如您所见,如果每个人都在做正确的事情(即调用超级(,MRO 将是第一父母、第二父母、第一父母的父母等等......

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

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

最新更新