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 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 ->对象

其他回答

关于@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,而这是这些“神奇变量”为数不多的真正有用和理智的使用之一。

在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 ->对象

在这种情况下,你试图继承的每个类都有自己的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)

整体

假设所有东西都来自object(如果不是你自己的),Python会根据你的类继承树计算一个方法解析顺序(MRO)。MRO满足3个性质:

一个阶层的孩子比他们的父母更重要 左父母先于右父母 一个类在MRO中只出现一次

如果不存在这样的顺序,Python将出错。它的内部工作原理是类祖先的C3线性化。点击这里阅读:https://www.python.org/download/releases/2.3/mro/

当一个方法被调用时,该方法在MRO中第一次出现就是被调用的方法。任何没有实现该方法的类都将被跳过。在该方法中对super的任何调用都将调用MRO中该方法的下一次出现。因此,在继承中放置类的顺序以及在方法中放置super调用的位置都很重要。

注意,你可以通过使用__mro__方法在python中看到MRO。

例子

下面所有的例子都有菱形类继承,如下所示:

    Parent
    /   \
   /     \
Left    Right
   \     /
    \   /
    Child

MRO是:

孩子 左 正确的 父

您可以通过调用Child来测试这一点。__mro__,返回:

(__main__.Child, __main__.Left, __main__.Right, __main__.Parent, object)

每一种方法都以超第一

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时

如果不是继承链中的所有类都调用super,那么继承顺序最重要。例如,如果Left不调用super,那么Right和Parent上的方法永远不会被调用:

class Parent(object):
    def __init__(self):
        print("parent")
        super(Parent, self).__init__()

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

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没有调用super, Parent仍然被跳过:

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

class Child(Left, Right):
    def __init__(self):
        print("child")
        super(Child, self).__init__()

在这里,Child()输出:

child
left
right

调用特定父节点上的方法

如果你想访问一个特定父类的方法,你应该直接引用这个类,而不是使用super。Super是关于遵循继承链,而不是到达特定类的方法。

下面是如何引用一个特定的父方法:

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):
        Parent.__init__(self)
        print("child")

在这种情况下,Child()输出:

parent
child
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

调用Third()定位在Third中定义的init。在这个例程中调用super调用First中定义的init。MRO =(一、二)。 现在在First中定义的init中调用super将继续搜索MRO并找到Second中定义的init,并且任何对super的调用都将命中默认对象init。我希望这个例子能够阐明这个概念。

如果你不在第一分局给管理员打电话。链条停止,您将得到以下输出。

first 10
that's it