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


当前回答

这就是我如何解决具有不同初始化变量的多重继承和具有相同函数调用的多个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)

其他回答

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

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

也许还可以添加一些东西,比如Django rest_framework和装饰器的一个小例子。这为隐含的问题提供了答案:“我为什么想要这个?”

如前所述:我们使用Django rest_framework,我们使用泛型视图,对于数据库中的每种类型的对象,我们发现我们有一个视图类为对象列表提供GET和POST,另一个视图类为单个对象提供GET、PUT和DELETE。

现在我们要用Django的login_required来装饰POST、PUT和DELETE。注意,这涉及到两个类,而不是两个类中的所有方法。

解决方案可以通过多重继承。

from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required

class LoginToPost:
    @method_decorator(login_required)
    def post(self, arg, *args, **kwargs):
        super().post(arg, *args, **kwargs)

其他方法也是如此。

在我的具体类的继承列表,我将添加我的LoginToPost之前ListCreateAPIView和LoginToPutOrDelete之前RetrieveUpdateDestroyAPIView。我的具体类的get将保持未修饰。

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