Python的super()如何与多重继承一起使用?


885

我在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方法解析顺序(MRO)有关。


实际上,多重继承是唯一super()有用的情况。我不建议将其与使用线性继承的类一起使用,因为这只是无用的开销。
巴绍

9
@Bachsau在技术上是正确的,因为它的开销很小,但是super()更具Python风格,并且允许随着时间的推移重构和更改代码。除非确实需要命名类特定的方法,否则请使用super()。
Paul Whipp

2
另一个问题super()是,它迫使每个子类也都使用它,而当不使用时super(),每个子类都可以自行决定。如果使用它的开发人员不知道super()或不知道使用过它,则可能会出现mro问题,很难跟踪。
巴绍(Bachsau)

Answers:


710

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将是 [Fourth, Second, Third, First].

顺便说一句:如果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'>)

12
当您开始在First,Second和Third [ pastebin.com/ezTyZ5Wa ]中调用super()时,它会变得更加有趣(并且可能会更加混乱)。
gatoatigrado 2012年

52
我认为,在第一堂课中没有超级呼叫是一个很大的问题。而没有讨论如何/为什么失去对问题的重要批判性理解。
山姆·哈特曼2013年

3
这个答案是完全错误的。没有父母的super()调用,什么都不会发生。@lifeless的答案是正确的。
塞林(Cerin)2015年

8
@Cerin本示例的重点是显示MRO的构造方式。该示例无意打印“第一\第二\第三”或其他内容。而MRO是确实是正确的:四.__ mro__ ==(<类的主体。第四'>,<类的。第二个'>,<类的。第三'>,<类的主要。首先'>,<类型'object'>)
rbp

2
据我所知,这个答案缺少OP的一个问题,那就是“如果要运行另一个问题该怎么办?”。我想看看这个问题的答案。我们只是应该显式命名基类吗?

251

您的代码和其他答案都是错误的。他们错过了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中找到下一个方法,这就是为什么First和Second也必须拥有它的原因,否则执行将在的结尾处停止Second.__init__()

这是我得到的:

>>> Third()
second
first
third

89
如果这些类需要不同的参数来初始化自己,该怎么办?
卡州

2
“合作子类化”
Quant Metropolis 2014年

5
这样,两个基本类的init方法都将被执行,而原始示例仅调用MRO中遇到的第一个init。我猜这是术语“合作子类化”所隐含的,但进行澄清将是有用的(您知道,“显式优于隐式”;))
Quant Metropolis 2014年

1
是的,如果您将不同的参数传递给通过super调用的方法,则该方法的所有实现(朝着MRO前进到object())都需要具有兼容的签名。这可以通过关键字参数来实现:接受比方法使用更多的参数,并忽略多余的参数。通常认为这样做很丑陋,并且在大多数情况下,添加新方法更好,但是init作为特殊方法名称(具有用户定义的参数)是唯一的(几乎是?)。
毫无生气的2014年

15
在python中,多重继承的设计确实非常糟糕。基类几乎需要知道谁将派生它,以及派生多少个其他基类,以及以什么顺序派生……否则 super将无法运行(由于参数不匹配),或者将不会调用很少的基础(因为您没有写super一个破坏链接的基础)!
纳瓦兹

185

我想一点点地阐述一下答案,因为当我开始阅读如何在Python的多继承层次结构中使用super()时,我没有立即得到它。

您需要了解的是,在完整继承层次结构的上下文中,根据使用的方法分辨率排序(MRO)算法super(MyClass, self).__init__()提供 __init__一种方法。

最后一部分对于理解至关重要。让我们再次考虑示例:

#!/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 撰写的有关方法解析顺序文章__init__在Python 2.3之前,使用“深度优先从左到右遍历”来计算解析顺序:

Third --> First --> object --> Second --> object

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

Third --> First --> Second --> object

因此,让我们跟随实例化Third类实例时发生的情况x = Third()

  1. 据MRO Third.__init__执行。
    • 版画 Third(): entering
    • 然后super(Third, self).__init__()执行,MRO返回First.__init__被调用。
  2. First.__init__ 执行。
    • 版画 First(): entering
    • 然后super(First, self).__init__()执行,MRO返回Second.__init__被调用。
  3. Second.__init__ 执行。
    • 版画 Second(): entering
    • 然后super(Second, self).__init__()执行,MRO返回object.__init__被调用。
  4. object.__init__ 执行(那里的代码中没有打印语句)
  5. 执行返回到Second.__init__随后打印Second(): exiting
  6. 执行返回到First.__init__随后打印First(): exiting
  7. 执行返回到Third.__init__随后打印Third(): exiting

这详细说明了为什么实例化Third()导致:

Third(): entering
First(): entering
Second(): entering
Second(): exiting
First(): exiting
Third(): exiting

MRO算法已从Python 2.3开始进行了改进,可以在复杂的情况下很好地工作,但是我猜想在大多数情况下,仍然可以使用“深度优先的从左到右遍历” +“删除重复的期望值”(请如果不是这种情况,请发表评论)。请务必阅读Guido的博客文章!


6
我仍然不明白为什么:内部初始化首超(第一,个体经营).__的init __()调用初始化其次的,因为那是什么MRO使然!
user389955

@ user389955创建的对象的类型为Third,具有所有init方法。因此,如果您假设MRO按照特定顺序创建了所有init函数的列表,则每次超级调用都将向前迈出一步,直到到达终点。
Sreekumar R

15
我认为第3步需要更多解释:如果Third没有继承自Secondsuper(First, self).__init__则将调用object.__init__并在返回后将打印“ first”。但由于Third来自继承FirstSecond,而不是调用object.__init__First.__init__的MRO使然,只有最后的呼叫object.__init__被保留,并在打印报表FirstSecond不直至达到object.__init__回报。由于Second是最后一次调用object.__init__,因此它在返回Second之前先返回内部First
MountainDrew

1
有趣的是,PyCharm似乎知道这一切(它暗示谈论哪些参数与调用超。它还具有输入协方差的一些想法去的,所以它识别List[subclass]为一个List[superclass],如果subclass是的子类superclassList来自typingPEP 483的模块IIRC)
Reb.Cabin

不错的帖子,但我想念有关构造函数参数的信息,即如果Second和First期望使用不同的参数会发生什么?First的构造函数将必须处理一些参数,并将其余参数传递给Second。那正确吗?对我来说,First需要了解Second所需的参数听起来并不正确。
克里斯蒂安·K。


28

这就是我解决以下问题的方法:具有用于初始化的不同变量的多个继承以及具有相同函数调用的多个MixIn。我必须显式地将变量添加到传递的** kwargs中,并添加一个MixIn接口作为超级调用的端点。

A是一个可扩展的基类,B并且C都是MixIn类,它们都提供功能fA并且Bv在他们的期望参数中__init__C期望w。该函数f采用一个参数yQ从所有三个类继承。MixInF对于混合接口BC


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)

我认为这可能应该是单独的问答,因为MRO本身就是一个足够大的主题,而无需处理具有继承关系的函数之间的不同论点(多重继承是其中的特例)。
毫无生气的2014年

8
从理论上讲,是的。实际上,每次我在python中遇到Diamond继承时都会出现这种情况,因此我在这里添加了它。因为,每次我都无法避免钻石继承时,这就是我要去的地方。以下是对未来我一些额外的链接:rhettinger.wordpress.com/2011/05/26/super-considered-super code.activestate.com/recipes/...
brent.payne

我们想要的是带有语义上有意义的参数名称的程序。但是在此示例中,几乎所有参数都是匿名命名的,这将使原始程序员难以编写代码并由另一个程序员读取代码。
亚瑟

对于具有描述性名称的github存储库的拉取请求将不胜感激
brent.payne

@ brent.payne我认为@Arthur意味着您的整个方法依赖于使用args/ kwargs而不是命名参数。
最大

24

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

只是需要注意的是,如果你做这种方式,你必须手动调用每一个为我敢肯定First__init__()不会被调用。


5
因为您没有调用每个继承的类,所以不会调用它。问题在于,如果FirstSecond都都继承了另一个类并直接调用它,那么该公共类(菱形的起点)将被调用两次。超级避免这种情况。
Trilarion 2014年

@Trilarion是的,我有信心不会。但是,我不太清楚,我也不想说像我做的那样,尽管那不太可能。关于object被两次叫是个好点。我没想到。我只是想指出您直接调用父类的意思。
Seaux

不幸的是,如果init尝试访问任何私有方法,则会中断操作:(
Erik Aronesty

21

总体

假设一切都源自object(如果不是),Python将基于您的类继承树计算方法解析顺序(MRO)。MRO满足3个属性:

  • 班上的孩子比父母先
  • 左父母先于右父母
  • 一个班级只在MRO中出现一次

如果不存在此类排序,则Python错误。它的内部工作原理是该类祖先的C3 Linerization。在此处阅读所有内容:https//www.python.org/download/releases/2.3/mro/

因此,在下面的两个示例中,它是:

  1. 儿童
  2. 剩下
  3. 父母

调用方法时,该方法在MRO中的首次出现就是被调用的方法。任何不实现该方法的类都将被跳过。super在该方法内的任何调用都将在MRO中调用该方法的下一次出现。因此,在继承中放置类的顺序以及在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):
        super(Child, self).__init__()
        print "child"

Child() 输出:

parent
right
left
child

随着super最后在每个方法

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() 输出:

child
left
right
parent

我发现您可以Left使用super()从访问Child。假设我Right要从内部访问Child。有没有一种方法来访问RightChild使用超?还是应该直接Right从内部致电super
alpha_989 '18

4
@ alpha_989如果只想访问特定类的方法,则应直接引用该类,而不要使用super。Super是关于继承链的,而不是特定类的方法。
扎格斯

1
感谢您明确提及“ A类仅在MRO中出现一次”。这解决了我的问题。现在,我终于了解了多重继承的工作原理。有人需要提到MRO的属性!
Tushar Vazirani

18

关于@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,这确实令人困惑。

我可能会有点烦,但是我注意到人们忘记了每次使用的时间*args以及**kwargs他们重写方法时,虽然这是对这些“魔术变量”真正有用且明智的使用方法之一。


哇,真丑。您不能只说您要调用哪个特定的超类,这是一种耻辱。尽管如此,这仍然给了我更多的动力去使用合成,避免像瘟疫一样多重继承。
汤姆·巴斯比

15

另一个尚未涵盖的要点是传递用于初始化类的参数。由于的目标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__

__init__直接调用超类来更直接地分配参数很容易,但是如果super超类中有任何调用和/或更改了MRO,并且根据实现的不同,可以多次调用A类,则失败。

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


5
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 中定义的super init的调用将继续搜索MRO并查找Second中定义的init,对super的任何调用都将命中默认对象init。我希望这个例子可以澄清这个概念。

如果您不从First致电super。链停止,您将获得以下输出。

first 10
that's it

1
这是因为在“第一课”中,您先调用“打印”,然后再调用“超级”。
rocky qi

2
这是为了说明呼叫顺序
Seraj Ahmad

4

在学习python的方式中,我学习了一个名为super()的东西,如果没有记错的话,这是一个内置函数。调用super()函数可以帮助继承通过父级和“同级”,并帮助您更清晰地了解。我仍然是一个初学者,但我喜欢分享我在python2.7中使用此super()的经验。

如果您已阅读了本页中的注释,则将听到方法解析顺序(MRO),该方法即为您编写的函数,MRO将使用深度从左到右的方案进行搜索和运行。您可以对此进行更多研究。

通过添加super()函数

super(First, self).__init__() #example for class First.

您可以通过添加每个实例和每个实例,将多个实例和“家族”与super()连接。然后它将执行这些方法,仔细检查它们,并确保您没有错过!但是,在之前或之后添加它们确实会有所不同,您将知道是否已经完成了学习Python的难度练习44。让乐趣开始吧!

以下面的示例为例,您可以复制并粘贴并尝试运行它:

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

它如何运行?third()的实例将像这样。每个步骤都在添加超级功能的类之间进行。

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)

现在,所有具有super()的类都已被访问!已经找到并执行了父类,现在它继续对继承中的函数进行拆箱以完成代码。

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)

对我来说,通过添加super()可以使我更清楚地了解python如何执行编码,并确保继承可以访问我想要的方法。


感谢您的详细演示!
Tushar Vazirani

3

我想补充@Visionscaper在顶部所说的内容:

Third --> First --> object --> Second --> object

在这种情况下,解释器不会过滤掉对象类,因为它是重复的,而是因为第二类出现在层次结构子集中的头部位置而没有出现在尾部位置。而对象仅出现在尾部位置,在C3算法中不能将其视为确定优先级的强项。

C类的线性度(mro)为

  • C级
  • 加上合并
    • 其父代P1,P2,.. = L(P1,P2,...)的线性化
    • 其父级P1,P​​2,...的列表

线性化合并是通过选择显示在列表开头而不是结尾的常见类来完成的,因为顺序很重要(下面将变得很清楚)

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"

“之所以这样,是因为“秒”出现在层次结构子集中的头部位置,而没有出现在尾部位置。” 目前尚不清楚头或尾的位置是什么,也不清楚层次结构的子集是什么,或者您所指的是哪个子集。
OrangeSherbet

尾部位置是指在类层次结构中较高的类,反之亦然。基类“对象”位于尾部的末尾。理解mro算法的关键是“第二”如何显示为“第一”的超级。我们通常假定它是“对象”类。没错,但是,仅在“头等舱”的角度来看。但是,从“第三”类的角度来看,“第一”的层次结构顺序是不同的,并且是按照上面的方法计算的。mro算法尝试为所有多个继承的类创建此透视图(或层次结构子集)
supi

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

第四->第三->第一->第二->基础->对象


2

也许仍然可以添加一些东西,一个带有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)

其他方法也一样。

在具体类的继承列表中,我要添加LoginToPostbefore ListCreateAPIViewLoginToPutOrDeletebefore RetrieveUpdateDestroyAPIView。我的具体课程get将保持不变。


1

发布此答案供我将来参考。

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班 object


1
A应该可以调用__init__A没有“发明”该方法__init__,因此不能假设其他类A的MRO 可能更早。唯一__init__不(也不应该)调用其方法的类super().__init__object
chepner

是的 这就是为什么我写了一个是object或许我想,我应该写class A (object) : ,而不是
AKHIL NADH PC

Aobject如果要向中添加参数,则不能这样__init__
chepner
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.