Python的super()如何处理多重继承?

2022-01-30 15:47:34 标签 pythonmultiple-inheritance

我是Python面向对象编程的新手,我遇到了一些麻烦

理解super() function (new sty函数(新样式类),特别是涉及到多重继承时。

例如,如果你有这样的东西:

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

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应该是[第一第二]还是[第二第一]?没有明显的期望,Python会抛出一个错误:

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

为什么上面的例子缺少super()调用?这些例子的重点是展示MRO是如何构造的。它们不打算打印“first\nsecond\third”或任何内容。您可以——当然也应该——尝试使用示例中的add super()调用,看看发生了什么,并对Python的继承模型有更深的理解。但我在这里的目标是保持简单,展示MRO是如何建造的。正如我所解释的那样:

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

###你的代码和其他答案都是bug。他们错过了super() calls in the first two classes that are required for co-operative subclas对协作子类化工作所需的前两个类的调用。

下面是该代码的固定版本:

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

###我想通过毫无生气的方式来详细说明这个答案,因为当我开始阅读如何在Python的多重继承层次中使用super()时,我并没有立即理解它。

你需要理解的是super(MyClass self)。__init__()根据所使用的方法解析排序(MRO)算法,在完整的继承层次结构上下文中提供了下一个__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

因此,让我们遵循当我们实例化第三类的一个实例时所发生的事情,例如x = Third()。

根据MROThird.__init__ executes.执行。

打印Third(): entering

然后super(Third self).__init__() exec执行和MRO返回First.__init__ which is called.这被称为。

第一。__init__执行。

打印First(): entering

然后super(First self).__init__() exec执行和MRO返回Second.__init__ which is called.这被称为。

第二。__init__执行。

打印Second(): entering

然后super(Second self).__init__() exec执行和MRO返回object.__init__ which is called.这被称为。

对象。__init__执行(代码中没有print语句)

执行回到Second.__init__ which then prints Second(): exiting

执行回到First.__init__ which then prints First(): exiting

执行回到Third.__init__ which then prints Third(): exiting

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

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

MRO算法从Python 2。3开始进行了改进,以在复杂的情况下工作得很好,但我猜使用“深度优先的从左到右遍历”+“删除重复,期望最后”在大多数情况下仍然有效(如果不是这样,请评论)。一定要阅读Guido的博文!

###这被称为菱形问题,页面上有一个Python条目,但简而言之,Python将从左到右调用超类的方法。

###这是我如何解决的问题,有多个继承与不同的变量进行初始化,并有多个mixin与相同的函数调用。我必须显式地向传递的**kwargs添加变量,并添加一个MixIn接口作为超级调用的端点。

这里A是一个可扩展的基类,B和C是MixIn类,它们都提供函数f。A和B在__init__中都期望形参v,而C期望形参w。

这个函数f takes one pa需要一个参数y. Q inh.Q in继承所有三个类。MixInF is the mixin interface for mixin接口是用来做什么的B and C.

C.

IPython笔记本的这个代码

Github Repo代码示例


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)

###我明白这并没有直接回答super() question but I feel it's relevant enough to share.<问题,但我觉得有足够的意义可以分享。

还有一种方法可以直接调用每个继承的类:


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__()不会被调用。

# # #整体

假设一切都是从对象派生而来的(如果不是,那就由你自己决定),Python会根据你的类继承树计算一个方法解析顺序(MRO)。MRO满足3个特性:

一个班级的孩子比他们的父母更重要

左父母优先于右父母

一个类在MRO中只出现一次

如果没有这样的排序存在Python错误。它的内部工作是类祖先的C3线性化。请点击这里阅读:https://www。Python。org/download/releases/2。3/mro/

因此,在下面的两个例子中都是:

孩子

正确的

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

注意,你可以通过使用__mro__方法在Python中看到MRO。的孩子。下面任何一个例子中的__mro__都返回:

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

例子

下面所有的例子都有一个像这样的类的菱形继承:

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

super firs每种方法的第一个

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
    

super last每种方法的最后一种

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

继承顺序是最重要的,如果不是所有类的继承调用超级。例如,如果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
left
right

###关于@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时,他们重写一个方法,而这是少数真正有用和理智的使用这些“魔术变量”。

###另一个尚未涉及的点是传递参数的初始化类。因为目的地super depends on the subclass the only good way to pass parameters is packing them all together. Then be careful to not取决于子类,传递参数的唯一好方法是将它们打包在一起。然后,注意不要让相同的参数名称具有不同的含义。

例子:

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__以更直接地赋值参数是很诱人的,但如果在超类中有任何超调用,并且/或MRO被更改,类a可能被调用多次,这取决于实现。

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

# # #考虑调用super().Foo() called from a sub-clas从子类调用。方法解析顺序(MRO)方法是解析方法调用的顺序。

案例1:单继承

在这个super()中,。 foo()将在层次结构中被搜索,如果发现else将考虑最近的实现引发一个Exception。“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之后,尽管C和D DO NOT have是它们之间的关系(但两者都与a有关系),但还是访问了D。这就是super()不同于单一继承的地方。

考虑一个稍微复杂一点的例子:

(红色数字表示MRO)

MRO是I E C H D A F G B对象

在这种情况下,我们从我到E c 。下一步将会是一个,但我们还没有去D A 。我们不能访问的一个子类D但是因为我们尚未访问H D 。树叶的一个子类H作为下一个类来访问。记住,如果可能的话,我们试图在层次结构中向上,所以我们访问了它最左边的超类D,在D之后,我们访问了A,但我们不能向上访问对象,因为我们还没有访问F G和b,这些类是为了为I完成MRO。

请注意,任何类在MRO中不能出现超过一次。

这就是super()在继承层次结构中查找的方式。

资源学分:Richard L Halterman Python编程基础

###在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来获得订单:

第四→第三→第一个→第二个→基地→对象

# # #

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 =(第一第二)。

现在调用在init中定义的super将继续搜索MRO并找到在Second中定义的init,任何对super的调用都将命中默认对象init。我希望这个例子能够澄清这个概念。

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

first 10
that's it

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

###我想补充一下@Visionscaper在顶部所说的话:

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

在这种情况下,解释器不会过滤掉对象类,因为它被复制了,而不是因为在层次子集中,Second出现在头位置而不是尾位置。而对象只出现在尾部位置,在C3算法中不被认为是确定优先级的强位置。

线性化(mro)的一类C L(C)是

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

###在学习Python的过程中,我学到了一个叫做super()的东西,如果没有错的话,这是一个内置函数。调用super()函数可以帮助继承传递给父类和“兄弟”类,并帮助你更清楚地看到。我仍然是一个初学者,但我喜欢分享我在Python2。7中使用这个super()的经验。

如果您通读了本页的注释,您将听到方法解析顺序(Method Resolution Order, MRO),该方法是您编写的MRO函数,将使用深度优先从左到右的方案来搜索和运行。你可以做更多的研究。

通过添加super()函数

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

你可以通过在super()中添加每个人和每个人来连接多个实例和' family '。它将执行这些方法,遍历它们,并确保您没有错过!然而,将它们添加在前面或后面确实会有所不同,你将知道你是否完成了learningPythonthehardway练习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()

它是如何运行的?第五个()的实例是这样的。每一步都是从一个类到另一个添加了超函数的类。

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

###也许还有一些东西可以添加到Django rest_framework和decorator的小例子中。这为一个隐含的问题提供了答案:“我为什么要这样做?”

如前所述:我们使用的是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)

其他方法也是如此。

在我的具体类的继承列表中,我会在ListCreateAPIView和RetrieveUpdateDestroyAPIView之前添加我的LoginToPost和LoginToPutOrDelete。我的具体类将保持不装饰。

###张贴这个答案供我将来参考。

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

# # #考虑孩子AB where parents 在父母A and BB have在构造函数中有关键字参数。

  A    B
   \  /
    AB

要初始化AB,你需要显式地调用父类构造函数,而不是使用super()。

例子:

class A():
    def __init__(self, a="a"):
        self.a = a
        print(f"a={a}")
    
    def A_method(self):
        print(f"A_method: {self.a}")
class B():
    def __init__(self, b="b"):
        self.b = b
        print(f"b={b}")
    
    def B_method(self):
        print(f"B_method: {self.b}")
    
    def magical_AB_method(self):
        print(f"magical_AB_method: {self.a}, {self.b}")
class AB(A,B):
    def __init__(self, a="A", b="B"):
        # super().__init__(a=a, b=b) # fails!
        A.__init__(self, a=a)
        B.__init__(self, b=b)
        self.A_method()
        self.B_method()
        self.magical_AB_method()
A()
>>> a=a
B()
>>> b=b
AB()
>>> a=A
>>> b=B
>>> A_method: A
>>> B_method: B

证明这两个父母都合并为孩子考虑magical_AB_method定义的内部类B从B方法的实例调用失败时,因为它没有访问成员变量在a。然而当从孩子AB的实例调用这个方法因为它继承了所需的成员变量一个。

B().magical_AB_method()
>>> AttributeError: 'B' object has no attribute 'a'
AB().magical_AB_method()
>>> magical_AB_method: A, B
阅读全文

▼ 版权说明

相关文章也很精彩
推荐内容
更多标签
相关热门
全站排行
随便看看

错说 cuoshuo.com —— 程序员的报错记录

部分内容根据CC版权协议转载;网站内容仅供参考,生产环境使用务必查阅官方文档

辽ICP备19011660号-5

×

扫码关注公众号:职场神器
发送: 1
获取永久解锁本站全部文章的验证码