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


当前回答

我想补充一下@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"

其他回答

也许还可以添加一些东西,比如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将保持未修饰。

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

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

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

考虑从子类调用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编程基础