super()如何处理多重继承?例如,给定:

class First(object):
    def __init__(self):
        print "first"

class Second(object):
    def __init__(self):
        print "second"

class Third(First, Second):
    def __init__(self):
        super(Third, self).__init__()
        print "that's it"

Third的哪个父方法执行super()。__init__ refer to?我可以选择哪些运行吗?

我知道这与方法解析顺序(MRO)有关。


当前回答

考虑子AB,父A和B在它们的构造函数中有关键字参数。

  A    B
   \  /
    AB

要初始化AB,需要显式调用父类构造函数,而不是使用super()。

例子:

class A():
    def __init__(self, a="a"):
        self.a = a
        print(f"a={a}")
    
    def A_method(self):
        print(f"A_method: {self.a}")

class B():
    def __init__(self, b="b"):
        self.b = b
        print(f"b={b}")
    
    def B_method(self):
        print(f"B_method: {self.b}")
    
    def magical_AB_method(self):
        print(f"magical_AB_method: {self.a}, {self.b}")

class AB(A,B):
    def __init__(self, a="A", b="B"):
        # super().__init__(a=a, b=b) # fails!
        A.__init__(self, a=a)
        B.__init__(self, b=b)
        self.A_method()
        self.B_method()
        self.magical_AB_method()


A()
>>> a=a

B()
>>> b=b

AB()
>>> a=A
>>> b=B
>>> A_method: A
>>> B_method: B

为了演示两个父类被组合到子类中,请考虑在类B中定义的magical_AB_method。当从B的实例调用时,该方法失败,因为它不能访问A中的成员变量。然而,当从子类AB的实例调用时,该方法工作,因为它从A继承了所需的成员变量。

B().magical_AB_method()
>>> AttributeError: 'B' object has no attribute 'a'

AB().magical_AB_method()
>>> magical_AB_method: A, B

其他回答

我想补充一下@Visionscaper在开头说的话:

Third --> First --> object --> Second --> object

在这种情况下,解释器不会过滤掉对象类,因为它是重复的,而是因为Second出现在一个层次结构子集的头部位置,而不是尾部位置。而在C3算法中,对象只出现在尾部位置,不被认为是一个强位置来确定优先级。

线性化(mro)的类C, L(C),是

丙类 加上归并 线性化父函数P1, P2, ..= L(P1, P2,… 它的父元素P1, P2, ..

线性化合并是通过选择出现在列表头部而不是尾部的公共类来完成的,因为顺序很重要(下面会清楚地说明)

Third的线性化计算如下:

    L(O)  := [O]  // the linearization(mro) of O(object), because O has no parents

    L(First)  :=  [First] + merge(L(O), [O])
               =  [First] + merge([O], [O])
               =  [First, O]

    // Similarly, 
    L(Second)  := [Second, O]

    L(Third)   := [Third] + merge(L(First), L(Second), [First, Second])
                = [Third] + merge([First, O], [Second, O], [First, Second])
// class First is a good candidate for the first merge step, because it only appears as the head of the first and last lists
// class O is not a good candidate for the next merge step, because it also appears in the tails of list 1 and 2, 
                = [Third, First] + merge([O], [Second, O], [Second])
// class Second is a good candidate for the second merge step, because it appears as the head of the list 2 and 3
                = [Third, First, Second] + merge([O], [O])            
                = [Third, First, Second, O]

因此,对于下面代码中的super()实现:

class First(object):
  def __init__(self):
    super(First, self).__init__()
    print "first"

class Second(object):
  def __init__(self):
    super(Second, self).__init__()
    print "second"

class Third(First, Second):
  def __init__(self):
    super(Third, self).__init__()
    print "that's it"

很明显,这个方法将如何解决

Third.__init__() ---> First.__init__() ---> Second.__init__() ---> 
Object.__init__() ---> returns ---> Second.__init__() -
prints "second" - returns ---> First.__init__() -
prints "first" - returns ---> Third.__init__() - prints "that's it"

我知道这并没有直接回答super()问题,但我觉得它有足够的相关性来分享。

还有一种方法可以直接调用每个继承的类:


class First(object):
    def __init__(self):
        print '1'

class Second(object):
    def __init__(self):
        print '2'

class Third(First, Second):
    def __init__(self):
        Second.__init__(self)

请注意,如果你这样做,你将不得不手动调用每个,因为我很确定First的__init__()不会被调用。

考虑子AB,父A和B在它们的构造函数中有关键字参数。

  A    B
   \  /
    AB

要初始化AB,需要显式调用父类构造函数,而不是使用super()。

例子:

class A():
    def __init__(self, a="a"):
        self.a = a
        print(f"a={a}")
    
    def A_method(self):
        print(f"A_method: {self.a}")

class B():
    def __init__(self, b="b"):
        self.b = b
        print(f"b={b}")
    
    def B_method(self):
        print(f"B_method: {self.b}")
    
    def magical_AB_method(self):
        print(f"magical_AB_method: {self.a}, {self.b}")

class AB(A,B):
    def __init__(self, a="A", b="B"):
        # super().__init__(a=a, b=b) # fails!
        A.__init__(self, a=a)
        B.__init__(self, b=b)
        self.A_method()
        self.B_method()
        self.magical_AB_method()


A()
>>> a=a

B()
>>> b=b

AB()
>>> a=A
>>> b=B
>>> A_method: A
>>> B_method: B

为了演示两个父类被组合到子类中,请考虑在类B中定义的magical_AB_method。当从B的实例调用时,该方法失败,因为它不能访问A中的成员变量。然而,当从子类AB的实例调用时,该方法工作,因为它从A继承了所需的成员变量。

B().magical_AB_method()
>>> AttributeError: 'B' object has no attribute 'a'

AB().magical_AB_method()
>>> magical_AB_method: A, B

在python 3.5+中,继承看起来是可预测的,对我来说非常好。 请看下面的代码:

class Base(object):
  def foo(self):
    print("    Base(): entering")
    print("    Base(): exiting")


class First(Base):
  def foo(self):
    print("   First(): entering Will call Second now")
    super().foo()
    print("   First(): exiting")


class Second(Base):
  def foo(self):
    print("  Second(): entering")
    super().foo()
    print("  Second(): exiting")


class Third(First, Second):
  def foo(self):
    print(" Third(): entering")
    super().foo()
    print(" Third(): exiting")


class Fourth(Third):
  def foo(self):
    print("Fourth(): entering")
    super().foo()
    print("Fourth(): exiting")

Fourth().foo()
print(Fourth.__mro__)

输出:

Fourth(): entering
 Third(): entering
   First(): entering Will call Second now
  Second(): entering
    Base(): entering
    Base(): exiting
  Second(): exiting
   First(): exiting
 Third(): exiting
Fourth(): exiting
(<class '__main__.Fourth'>, <class '__main__.Third'>, <class '__main__.First'>, <class '__main__.Second'>, <class '__main__.Base'>, <class 'object'>)

正如你所看到的,它对每个继承链调用foo一次,其顺序与继承链的顺序相同。你可以通过调用.mro来获得订单:

Fourth -> Third -> First -> Second -> Base ->对象

这就是我如何解决具有不同初始化变量的多重继承和具有相同函数调用的多个mixin的问题。我必须显式地为传递的**kwargs添加变量,并添加一个MixIn接口作为超级调用的端点。

这里A是一个可扩展的基类,B和C是MixIn类,它们都提供函数f。A和B都在它们的__init__中期望参数v,而C期望w。 函数f接受一个参数y。Q继承了所有三个类。MixInF是B和C的mixin接口。

这段代码的IPython NoteBook Github回购的代码示例


class A(object):
    def __init__(self, v, *args, **kwargs):
        print "A:init:v[{0}]".format(v)
        kwargs['v']=v
        super(A, self).__init__(*args, **kwargs)
        self.v = v


class MixInF(object):
    def __init__(self, *args, **kwargs):
        print "IObject:init"
    def f(self, y):
        print "IObject:y[{0}]".format(y)


class B(MixInF):
    def __init__(self, v, *args, **kwargs):
        print "B:init:v[{0}]".format(v)
        kwargs['v']=v
        super(B, self).__init__(*args, **kwargs)
        self.v = v
    def f(self, y):
        print "B:f:v[{0}]:y[{1}]".format(self.v, y)
        super(B, self).f(y)


class C(MixInF):
    def __init__(self, w, *args, **kwargs):
        print "C:init:w[{0}]".format(w)
        kwargs['w']=w
        super(C, self).__init__(*args, **kwargs)
        self.w = w
    def f(self, y):
        print "C:f:w[{0}]:y[{1}]".format(self.w, y)
        super(C, self).f(y)


class Q(C,B,A):
    def __init__(self, v, w):
        super(Q, self).__init__(v=v, w=w)
    def f(self, y):
        print "Q:f:y[{0}]".format(y)
        super(Q, self).f(y)