Python';s super()是否可以处理多重继承?

Python';s super()是否可以处理多重继承?,python,multiple-inheritance,Python,Multiple Inheritance,我是Python面向对象编程的新手,遇到了麻烦 了解super()函数(新样式类),尤其是涉及多重继承时 例如,如果您有以下内容: class First(object): def __init__(self): print "first" class Second(object): def __init__(self): print "second" class Third(First, Second)

我是Python面向对象编程的新手,遇到了麻烦 了解
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()
一起运行,为什么


如果你想运行另一个呢?我知道这与Python方法解析顺序()有关。

这被称为,页面上有一个关于Python的条目,但简而言之,Python将从左到右调用超类的方法

Guido在他的博客文章(包括之前的两次尝试)中详细介绍了这一点

在您的示例中,
Third()
将首先调用
。\uuuu init\uuuu
。Python在类的父级中查找从左到右列出的每个属性。在本例中,我们正在查找
\uuuu init\uuuu
。所以,如果你定义

class Third(First, Second):
    ...
Python将首先查看
第一个
,如果
第一个
没有该属性,那么它将查看
第二个

当继承开始交叉路径时,这种情况变得更加复杂(例如,如果继承自
Second
First
)。阅读上面的链接了解更多详细信息,但简而言之,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
Edit:我看到一些人认为上面的示例缺少
super()
调用,所以让我解释一下:示例的目的是展示MRO是如何构造的。它们不是用于打印“第一\n第二\n第三”或任何内容的。当然,您可以——也应该——仔细研究一下这个示例,添加
super()
调用,看看会发生什么,并深入了解Python的继承模型。但我的目标是保持它的简单性,并展示MRO是如何构建的。正如我所解释的那样:

>>> Fourth.__mro__
(<class '__main__.Fourth'>,
 <class '__main__.Second'>, <class '__main__.Third'>,
 <class '__main__.First'>,
 <type 'object'>)
第四个__ (, , , )
您的代码和其他答案都有问题。它们缺少前两个类中的
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")
super()
调用在每个步骤都会在MRO中找到下一个方法,这就是为什么第一个和第二个也必须有它,否则执行会在
第二个步骤结束时停止

这就是我得到的:

>>> Third()
second
first
third

我知道这并不能直接回答
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)

请注意,如果您这样做,您将不得不手动调用每个函数,因为我非常确定
首先
\uuu init\uuu()
不会被调用

另一个尚未涉及的问题是传递初始化类的参数。由于
super
的目的地取决于子类,因此传递参数的唯一好方法是将它们打包在一起。然后注意不要使用具有不同含义的相同参数名称

例如:

class A(object):
    def __init__(self, **kwargs):
        print('A.__init__')
        super().__init__()

class B(A):
    def __init__(self, **kwargs):
        print('B.__init__ {}'.format(kwargs['x']))
        super().__init__(**kwargs)


class C(A):
    def __init__(self, **kwargs):
        print('C.__init__ with {}, {}'.format(kwargs['a'], kwargs['b']))
        super().__init__(**kwargs)


class D(B, C): # MRO=D, B, C, A
    def __init__(self):
        print('D.__init__')
        super().__init__(a=1, b=2, x=3)

print(D.mro())
D()
给出:

[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
D.__init__
B.__init__ 3
C.__init__ with 1, 2
A.__init__
[,]
D.。u_init__
B.uuu初始值3
C.uuu init_uuuuuu与1,2
A.._uinit__
直接调用超级类
\uuuu init\uuuu
以更直接地分配参数是很有诱惑力的,但如果在超级类中有任何
super
调用和/或MRO发生更改,并且根据实现情况,可能会多次调用类a,则调用会失败


总而言之:协作继承和用于初始化的超级和特定参数不能很好地协同工作。

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

这里
A
是一个可扩展的基类,
B
C
是混合类,它们都提供函数
f
A
B
在它们的
\uuuu init\uuuu
中都期望参数
v
,而
C
期望
w
。 函数
f
采用一个参数
y
Q
继承自所有三个类
MixInF
B
C
的mixin接口

我想详细说明一下,因为当我开始阅读Python中如何在多重继承层次结构中使用super()时,我并没有立即理解

您需要了解的是,
super(MyClass,self)。\uuuu init\uuu()
根据完整继承层次结构上下文中使用的方法解析排序(MRO)算法提供下一个
\uuuu init\uuu
方法

最后一部分是理解的关键。让我们再考虑一下这个例子:

#!/usr/bin/env python2

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

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

class Third(First, Second):
  def __init__(self):
    print "Third(): entering"
    super(Third, self).__init__()
    print "Third(): exiting"
Guido van Rossum使用“深度优先左向右遍历”计算解析
\uuu init\uuuu
的顺序(在Python 2.3之前):

删除所有重复项(最后一项除外)后,我们得到:

Third --> First --> Second --> object
那么,让我们来看看当我们实例化
第三个
类的实例时会发生什么,例如Third --> First --> object --> Second --> object
Third --> First --> Second --> object
Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting
class First(object):
  def __init__(self, a):
    print "first", a
    super(First, self).__init__(20)

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

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

t = Third()
first 10
second 20
that's it
first 10
that's it
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)
class A(object):
  def __init__(self, *args, a, **kwargs):
    print("A", a)
Third --> First --> object --> Second --> object
    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]
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"
class Parent(object):
    def __init__(self):
        super(Parent, self).__init__()
        print "parent"

class Left(Parent):
    def __init__(self):
        super(Left, self).__init__()
        print "left"

class Right(Parent):
    def __init__(self):
        super(Right, self).__init__()
        print "right"

class Child(Left, Right):
    def __init__(self):
        super(Child, self).__init__()
        print "child"
parent
right
left
child
class Parent(object):
    def __init__(self):
        print "parent"
        super(Parent, self).__init__()

class Left(Parent):
    def __init__(self):
        print "left"
        super(Left, self).__init__()

class Right(Parent):
    def __init__(self):
        print "right"
        super(Right, self).__init__()

class Child(Left, Right):
    def __init__(self):
        print "child"
        super(Child, self).__init__()
child
left
right
parent
super(First, self).__init__() #example for class First.
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()
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)
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)
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'>)
    A
   / \
  B   C
   \ /
    D
class A:
    def __init__(self, name=None):
        #  this is the head of the diamond, no need to call super() here
        self.name = name

class B(A):
    def __init__(self, param1='hello', **kwargs):
        super().__init__(**kwargs)
        self.param1 = param1

class C(A):
    def __init__(self, param2='bye', **kwargs):
        super().__init__(**kwargs)
        self.param2 = param2

class D(B, C):
    def __init__(self, works='fine', **kwargs):
        super().__init__(**kwargs)
        print(f"{works=}, {self.param1=}, {self.param2=}, {self.name=}")

d = D(name='Testing')