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

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

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

I’m pretty much new in Python object oriented programming and I have trouble understanding the super() function (new style classes) especially when it comes to multiple inheritance.

For example if you have something like:

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"

What I don’t get is: will the Third() class inherit both constructor methods? If yes, then which one will be run with super() and why?

And what if you want to run the other one? I know it has something to do with Python method resolution order (MRO).


回答 0

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

This is detailed with a reasonable amount of detail by Guido himself in his blog post Method Resolution Order (including two earlier attempts).

In your example, Third() will call First.__init__. Python looks for each attribute in the class’s parents as they are listed left to right. In this case, we are looking for __init__. So, if you define

class Third(First, Second):
    ...

Python will start by looking at First, and, if First doesn’t have the attribute, then it will look at Second.

This situation becomes more complex when inheritance starts crossing paths (for example if First inherited from Second). Read the link above for more details, but, in a nutshell, Python will try to maintain the order in which each class appears on the inheritance list, starting with the child class itself.

So, for instance, if you had:

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"

the MRO would be [Fourth, Second, Third, First].

By the way: if Python cannot find a coherent method resolution order, it’ll raise an exception, instead of falling back to behavior which might surprise the user.

Edited to add an example of an ambiguous 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"

Should Third‘s MRO be [First, Second] or [Second, First]? There’s no obvious expectation, and Python will raise an error:

TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution order (MRO) for bases Second, First

Edit: I see several people arguing that the examples above lack super() calls, so let me explain: The point of the examples is to show how the MRO is constructed. They are not intended to print “first\nsecond\third” or whatever. You can – and should, of course, play around with the example, add super() calls, see what happens, and gain a deeper understanding of Python’s inheritance model. But my goal here is to keep it simple and show how the MRO is built. And it is built as I explained:

>>> Fourth.__mro__
(<class '__main__.Fourth'>,
 <class '__main__.Second'>, <class '__main__.Third'>,
 <class '__main__.First'>,
 <type 'object'>)

回答 1

您的代码和其他答案都是错误的。他们错过了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

Your code, and the other answers, are all buggy. They are missing the super() calls in the first two classes that are required for co-operative subclassing to work.

Here is a fixed version of the code:

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

The super() call finds the next method in the MRO at each step, which is why First and Second have to have it too, otherwise execution stops at the end of Second.__init__().

This is what I get:

>>> Third()
second
first
third

回答 2

我想一点点地阐述一下答案,因为当我开始阅读如何在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的博客文章!

I wanted to elaborate the answer by lifeless a bit because when I started reading about how to use super() in a multiple inheritance hierarchy in Python, I did’t get it immediately.

What you need to understand is that super(MyClass, self).__init__() provides the next __init__ method according to the used Method Resolution Ordering (MRO) algorithm in the context of the complete inheritance hierarchy.

This last part is crucial to understand. Let’s consider the example again:

#!/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"

According to this article about Method Resolution Order by Guido van Rossum, the order to resolve __init__ is calculated (before Python 2.3) using a “depth-first left-to-right traversal” :

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

After removing all duplicates, except for the last one, we get :

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

So, lets follow what happens when we instantiate an instance of the Third class, e.g. x = Third().

  1. According to MRO Third.__init__ executes.
    • prints Third(): entering
    • then super(Third, self).__init__() executes and MRO returns First.__init__ which is called.
  2. First.__init__ executes.
    • prints First(): entering
    • then super(First, self).__init__() executes and MRO returns Second.__init__ which is called.
  3. Second.__init__ executes.
    • prints Second(): entering
    • then super(Second, self).__init__() executes and MRO returns object.__init__ which is called.
  4. object.__init__ executes (no print statements in the code there)
  5. execution goes back to Second.__init__ which then prints Second(): exiting
  6. execution goes back to First.__init__ which then prints First(): exiting
  7. execution goes back to Third.__init__ which then prints Third(): exiting

This details out why instantiating Third() results in to :

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

The MRO algorithm has been improved from Python 2.3 onwards to work well in complex cases, but I guess that using the “depth-first left-to-right traversal” + “removing duplicates expect for the last” still works in most cases (please comment if this is not the case). Be sure to read the blog post by Guido!


回答 3

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

This is known as the Diamond Problem, the page has an entry on Python, but in short, Python will call the superclass’s methods from left to right.


回答 4

这就是我解决以下问题的方法:具有用于初始化的不同变量的多个继承以及具有相同函数调用的多个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)

This is to how I solved to issue of having multiple inheritance with different variables for initialization and having multiple MixIns with the same function call. I had to explicitly add variables to passed **kwargs and add a MixIn interface to be an endpoint for super calls.

Here A is an extendable base class and B and C are MixIn classes both who provide function f. A and B both expect parameter v in their __init__ and C expects w. The function f takes one parameter y. Q inherits from all three classes. MixInF is the mixin interface for B and C.


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)

回答 5

我知道这并不能直接回答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__()不会被调用。

I understand this doesn’t directly answer the super() question, but I feel it’s relevant enough to share.

There is also a way to directly call each inherited class:


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)

Just note that if you do it this way, you’ll have to call each manually as I’m pretty sure First‘s __init__() won’t be called.


回答 6

总体

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

  • 班上的孩子比父母先
  • 左父母先于右父母
  • 一个Class只在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

Overall

Assuming everything descends from object (you are on your own if it doesn’t), Python computes a method resolution order (MRO) based on your class inheritance tree. The MRO satisfies 3 properties:

  • Children of a class come before their parents
  • Left parents come before right parents
  • A class only appears once in the MRO

If no such ordering exists, Python errors. The inner workings of this is a C3 Linerization of the classes ancestry. Read all about it here: https://www.python.org/download/releases/2.3/mro/

Thus, in both of the examples below, it is:

  1. Child
  2. Left
  3. Right
  4. Parent

When a method is called, the first occurrence of that method in the MRO is the one that is called. Any class that doesn’t implement that method is skipped. Any call to super within that method will call the next occurrence of that method in the MRO. Consequently, it matters both what order you place classes in inheritance, and where you put the calls to super in the methods.

With super first in each method

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

parent
right
left
child

With super last in each method

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

child
left
right
parent

回答 7

关于@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他们重写方法时,虽然这是对这些“魔术变量”真正有用且明智的使用方法之一。

About @calfzhou’s comment, you can use, as usually, **kwargs:

Online running example

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)

Result:

A None
B hello
A1 6
B1 5

You can also use them positionally:

B1(5, 6, b="hello", a=None)

but you have to remember the MRO, it’s really confusing.

I can be a little annoying, but I noticed that people forgot every time to use *args and **kwargs when they override a method, while it’s one of few really useful and sane use of these ‘magic variables’.


回答 8

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

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

Another not yet covered point is passing parameters for initialization of classes. Since the destination of super depends on the subclass the only good way to pass parameters is packing them all together. Then be careful to not have the same parameter name with different meanings.

Example:

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

gives:

[<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__

Calling the super class __init__ directly to more direct assignment of parameters is tempting but fails if there is any super call in a super class and/or the MRO is changed and class A may be called multiple times, depending on the implementation.

To conclude: cooperative inheritance and super and specific parameters for initialization aren’t working together very well.


回答 9

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

Output is

first 10
second 20
that's it

Call to Third() locates the init defined in Third. And call to super in that routine invokes init defined in First. MRO=[First, Second]. Now call to super in init defined in First will continue searching MRO and find init defined in Second, and any call to super will hit the default object init. I hope this example clarifies the concept.

If you don’t call super from First. The chain stops and you will get the following output.

first 10
that's it

回答 10

在学习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如何执行编码,并确保继承可以访问我想要的方法。

In learningpythonthehardway I learn something called super() an in-built function if not mistaken. Calling super() function can help the inheritance to pass through the parent and ‘siblings’ and help you to see clearer. I am still a beginner but I love to share my experience on using this super() in python2.7.

If you have read through the comments in this page, you will hear of Method Resolution Order (MRO), the method being the function you wrote, MRO will be using Depth-First-Left-to-Right scheme to search and run. You can do more research on that.

By adding super() function

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

You can connect multiple instances and ‘families’ with super(), by adding in each and everyone in them. And it will execute the methods, go through them and make sure you didn’t miss out! However, adding them before or after does make a difference you will know if you have done the learningpythonthehardway exercise 44. Let the fun begins!!

Taking example below, you can copy & paste and try run it:

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

How does it run? The instance of fifth() will goes like this. Each step goes from class to class where the super function added.

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)

The parent was found and it will go continue to Third and Fourth!!

5.) print("third (before)")
6.) super()> First (Parent class)
7.) print ("Fourth (before)")
8.) super()> First (Parent class)

Now all the classes with super() have been accessed! The parent class has been found and executed and now it continues to unbox the function in the inheritances to finished the codes.

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

The outcome of the program above:

fifth (before)
second (before
third (before)
fourth (before)
first
fourth (after)
third (after)
second (after)
fifth (after)

For me by adding super() allows me to see clearer on how python would execute my coding and make sure the inheritance can access the method I intended.


回答 11

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

I would like to add to what @Visionscaper says at the top:

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

In this case the interpreter doesnt filter out the object class because its duplicated, rather its because Second appears in a head position and doesnt appear in the tail position in a hierarchy subset. While object only appears in tail positions and is not considered a strong position in C3 algorithm to determine priority.

The linearisation(mro) of a class C, L(C), is the

  • the Class C
  • plus the merge of
    • linearisation of its parents P1, P2, .. = L(P1, P2, …) and
    • the list of its parents P1, P2, ..

Linearised Merge is done by selecting the common classes that appears as the head of lists and not the tail since order matters(will become clear below)

The linearisation of Third can be computed as follows:

    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]

Thus for a super() implementation in the following code:

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"

it becomes obvious how this method will be resolved

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"

回答 12

在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

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

In python 3.5+ inheritance looks predictable and very nice for me. Please looks at this code:

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

Outputs:

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

As you can see, it calls foo exactly ONE time for each inherited chain in the same order as it was inherited. You can get that order by calling .mro :

Fourth -> Third -> First -> Second -> Base -> object


回答 13

也许仍然可以添加一些东西,一个带有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将保持不变。

Maybe there’s still something that can be added, a small example with Django rest_framework, and decorators. This provides an answer to the implicit question: “why would I want this anyway?”

As said: we’re with Django rest_framework, and we’re using generic views, and for each type of objects in our database we find ourselves with one view class providing GET and POST for lists of objects, and an other view class providing GET, PUT, and DELETE for individual objects.

Now the POST, PUT, and DELETE we want to decorate with Django’s login_required. Notice how this touches both classes, but not all methods in either class.

A solution could go through multiple inheritance.

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)

Likewise for the other methods.

In the inheritance list of my concrete classes, I would add my LoginToPost before ListCreateAPIView and LoginToPutOrDelete before RetrieveUpdateDestroyAPIView. My concrete classes’ get would stay undecorated.


回答 14

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

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

Posting this answer for my future referance.

Python Multiple Inheritance should use a diamond model and the function signature shouldn’t change in the model.

    A
   / \
  B   C
   \ /
    D

The sample code snippet would be ;-

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

Here class A is object