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)有关。


当前回答

关于@calfzhou的评论,你可以像往常一样使用**kwargs:

在线运行示例

class A(object):
  def __init__(self, a, *args, **kwargs):
    print("A", a)

class B(A):
  def __init__(self, b, *args, **kwargs):
    super(B, self).__init__(*args, **kwargs)
    print("B", b)

class A1(A):
  def __init__(self, a1, *args, **kwargs):
    super(A1, self).__init__(*args, **kwargs)
    print("A1", a1)

class B1(A1, B):
  def __init__(self, b1, *args, **kwargs):
    super(B1, self).__init__(*args, **kwargs)
    print("B1", b1)


B1(a1=6, b1=5, b="hello", a=None)

结果:

A None
B hello
A1 6
B1 5

你也可以在不同的位置使用它们:

B1(5, 6, b="hello", a=None)

但你必须记住MRO,它真的很混乱。你可以通过使用关键字参数来避免这种情况:

class A(object):
  def __init__(self, *args, a, **kwargs):
    print("A", a)

等等。

我可能有点烦人,但我注意到人们每次重写一个方法时都会忘记使用*args和**kwargs,而这是这些“神奇变量”为数不多的真正有用和理智的使用之一。

其他回答

你的代码和其他答案都是错误的。它们缺少前两个类中的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("third")

输出:

>>> Third()
second
first
third

super()调用在每一步都在MRO中查找下一个方法,这就是为什么First和Second也必须拥有它,否则执行将在Second.__init__()结束时停止。


如果在First和Second中没有super()调用,输出就会丢失Second:

>>> Third()
first
third

Guido在他的博客文章Method Resolution Order(包括两个早期的尝试)中对此进行了详细的描述。

在你的例子中,Third()将调用First.__init__。Python按照从左到右列出的顺序在类的父类中查找每个属性。在本例中,我们正在寻找__init__。如果你定义

class Third(First, Second):
    ...

Python将首先查看First,如果First没有该属性,那么它将查看Second。

当继承开始交叉路径时(例如First从Second继承),这种情况会变得更加复杂。阅读上面的链接了解更多细节,但是,简而言之,Python将试图保持每个类在继承列表中出现的顺序,从子类本身开始。

例如,如果你有:

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

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

class Third(First):
    def __init__(self):
        print "third"

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

MRO为[第四、第二、第三、第一]。

顺便说一下:如果Python无法找到一致的方法解析顺序,它将引发异常,而不是退回到可能使用户感到惊讶的行为。

模棱两可的MRO示例:

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

class Third(First, Second):
    def __init__(self):
        print "third"

Third的MRO是[First, Second]还是[Second, First]?没有明显的期望,Python将引发一个错误:

TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution order (MRO) for bases Second, First

为什么上面的例子没有super()调用?这些示例的重点是展示MRO是如何构造的。他们不打算打印“第一\第二\第三”或其他什么。当然,您可以(也应该)尝试一下这个示例,添加super()调用,看看会发生什么,并更深入地理解Python的继承模型。但我的目标是保持简单,并展示MRO是如何构建的。正如我解释的那样:

>>> Fourth.__mro__
(<class '__main__.Fourth'>,
 <class '__main__.Second'>, <class '__main__.Third'>,
 <class '__main__.First'>,
 <type 'object'>)

在学习python的过程中,我学到了一个叫做super()的东西,如果没有弄错的话,这是一个内置函数。调用super()函数可以帮助继承通过父节点和“兄弟节点”传递,并帮助你看得更清楚。我仍然是初学者,但我喜欢分享我在python2.7中使用这个super()的经验。

如果您已经阅读了本页中的注释,您将听说方法解析顺序(MRO),该方法是您编写的函数,MRO将使用深度优先的左至右方案来搜索和运行。你可以做更多的研究。

通过添加super()函数

super(First, self).__init__() #example for class First.

你可以用super()连接多个实例和“家族”,方法是添加其中的每个实例和每个人。它会执行这些方法,检查它们,确保你没有错过!然而,在之前或之后添加它们确实会有区别,你会知道你是否已经通过硬路练习学习了python。让乐趣开始吧!!

以下面的例子为例,你可以复制粘贴并试着运行它:

class First(object):
    def __init__(self):

        print("first")

class Second(First):
    def __init__(self):
        print("second (before)")
        super(Second, self).__init__()
        print("second (after)")

class Third(First):
    def __init__(self):
        print("third (before)")
        super(Third, self).__init__()
        print("third (after)")


class Fourth(First):
    def __init__(self):
        print("fourth (before)")
        super(Fourth, self).__init__()
        print("fourth (after)")


class Fifth(Second, Third, Fourth):
    def __init__(self):
        print("fifth (before)")
        super(Fifth, self).__init__()
        print("fifth (after)")

Fifth()

它是如何运行的?fifth()的实例如下所示。每一步从一个类到另一个类,其中添加了超函数。

1.) print("fifth (before)")
2.) super()>[Second, Third, Fourth] (Left to right)
3.) print("second (before)")
4.) super()> First (First is the Parent which inherit from object)

父母已经找到了,会继续到第三和第四!!

5.) print("third (before)")
6.) super()> First (Parent class)
7.) print ("Fourth (before)")
8.) super()> First (Parent class)

现在所有带有super()的类都已经被访问了!父类已经找到并执行,现在它继续在继承中解箱函数以完成代码。

9.) print("first") (Parent)
10.) print ("Fourth (after)") (Class Fourth un-box)
11.) print("third (after)") (Class Third un-box)
12.) print("second (after)") (Class Second un-box)
13.) print("fifth (after)") (Class Fifth un-box)
14.) Fifth() executed

以上方案的成果:

fifth (before)
second (before
third (before)
fourth (before)
first
fourth (after)
third (after)
second (after)
fifth (after)

对我来说,添加super()可以让我更清楚地看到python如何执行我的代码,并确保继承可以访问我想要的方法。

考虑子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"