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


当前回答

把这个答案贴出来供我将来参考。

Python多重继承应该使用菱形模型,并且函数签名不应该在模型中更改。

    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')

这里类A是对象

其他回答

关于@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(). foo()。方法解析顺序(MRO)方法是解析方法调用的顺序。

案例1:单继承

在这种情况下,super(). foo()将在层次结构中向上搜索,并将考虑最接近的实现,如果找到,否则引发异常。在任何被访问的子类和它在层次结构上的超类之间,“is a”关系将始终为True。但是这个故事在多重继承中并不总是一样的。

案例2:多重继承

在这里,当搜索super(). foo()实现时,层次结构中每个被访问的类都可能是一个关系,也可能不是。考虑以下例子:

class A(object): pass
class B(object): pass
class C(A): pass
class D(A): pass
class E(C, D): pass
class F(B): pass
class G(B): pass
class H(F, G): pass
class I(E, H): pass

这里,I是层次结构中最低的类。层次图和MRO为我将

(红色数字为MRO)

MRO是I E C D A H F G B对象

注意,类X只有在继承自它的所有子类都被访问过的情况下才会被访问。例如,如果一个类下面有一个箭头,而你还没有访问过这个箭头,那么你永远不应该访问这个类)。

在这里,注意在访问类C之后,D被访问,尽管C和D DO NOT have是它们之间的关系(但它们都与a有关系)。这是super()不同于单一继承的地方。

考虑一个稍微复杂一点的例子:

(红色数字为MRO)

MRO是I E C H D A F G B对象

在这种情况下,我们从I到E再到c。下一步是A,但我们还没有访问A的子类D。然而,我们不能访问D,因为我们还没有访问D的子类H。剩下H作为下一个要访问的类。记住,如果可能的话,我们试图在层次结构中向上,所以我们访问它最左边的超类D。在D之后,我们访问A,但我们不能向上到object,因为我们还没有访问F、G和b。这些类,依次,为I完成MRO。

注意,任何类都不能在MRO中出现超过一次。

这就是super()在继承层次结构中的查找方式。

资源来源:Richard L Halterman Python编程基础

这就是所谓的钻石问题,该页面有一个关于Python的条目,但简而言之,Python将从左到右调用超类的方法。

在这种情况下,你试图继承的每个类都有自己的init位置参数,只需调用每个类自己的init方法,如果试图继承多个对象,则不要使用super。

class A():
    def __init__(self, x):
        self.x = x

class B():
    def __init__(self, y, z):
        self.y = y
        self.z = z

class C(A, B):
    def __init__(self, x, y, z):
        A.__init__(self, x)
        B.__init__(self, y, z)

>>> c = C(1,2,3)
>>>c.x, c.y, c.z 
(1, 2, 3)

把这个答案贴出来供我将来参考。

Python多重继承应该使用菱形模型,并且函数签名不应该在模型中更改。

    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')

这里类A是对象