我是Python面向对象编程的新手,遇到了一些麻烦理解
例如:
1 2 3 4 5 6 7 8 9 10 11 12 | 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" |
我不明白的是:
如果你想运行另一个呢?我知道这与Python方法解析顺序(MRO)有关。
Guido在他的博客文章方法解析顺序(包括之前的两次尝试)中给出了相当多的细节。
在您的示例中,
1 2 | class Third(First, Second): ... |
Python将从查看
当继承开始交叉路径时,这种情况变得更加复杂(例如,如果
例如,如果你有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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的例子:
1 2 3 4 5 6 7 8 9 10 11 | 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" |
1 2 | TypeError: Error when calling the metaclass bases Cannot create a consistent method resolution order (MRO) for bases Second, First |
编辑:我看到一些人认为上面的例子缺少
1 2 3 4 5 | >>> Fourth.__mro__ (<class '__main__.Fourth'>, <class '__main__.Second'>, <class '__main__.Third'>, <class '__main__.First'>, <type 'object'>) |
您的代码和其他答案都有bug。它们缺少前两个类中的
以下是固定版本的守则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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") |
我得到的是:
1 2 3 4 | >>> Third() second first third |
我想用无生气的方式来详细说明这个答案,因为当我开始阅读如何在Python的多重继承层次结构中使用super()时,我并没有立即得到它。
您需要了解的是,
最后一部分是理解的关键。让我们再考虑一下这个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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" |
根据Guido van Rossum关于方法解析顺序的这篇文章,解析
1 | Third --> First --> object --> Second --> object |
除最后一个副本外,删除所有副本后,得到:
1 | Third --> First --> Second --> object |
因此,让我们看看在实例化
根据亚南先生的说法,第三个叫做第一。
接下来,根据MRO,在
self).__init__()
第一个的
第二次
这就详细说明了为什么实例化Third()会导致:
1 2 3 4 | >>> x = Third() second first that's it |
MRO算法已经从Python 2.3开始进行了改进,可以在复杂的情况下很好地工作,但是我认为在大多数情况下,使用"深度优先的从左到右遍历"+"除最后一个外删除重复项"仍然可以工作(如果不是这样,请评论)。请务必阅读Guido的博客文章!
这就是众所周知的Diamond问题,页面上有一个关于Python的条目,但是简而言之,Python将从左到右调用超类的方法。
这是我如何解决的问题,有多个继承与不同的变量初始化和有多个混合与相同的函数调用。我必须显式地向传递的**kwargs添加变量,并添加MixIn接口作为超级调用的端点。
这里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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) |
我知道这并不能直接回答
还有一种方法可以直接调用每个继承的类:
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)
请注意,如果您这样做,您将不得不手动调用每一个,因为我非常确定
整体
假设所有内容都来自
如果不存在这样的排序,则Python错误。其内部工作原理是类祖先的C3线性化。请在这里阅读:https://www.python.org/download/releases/2.3/mro/
因此,在下面的两个例子中,它是:
孩子左正确的父当一个方法被调用时,该方法在MRO中的第一个出现就是被调用的那个。任何没有实现该方法的类都将被跳过。该方法中对
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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" |
1 2 3 4 | parent right left child |
与
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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__() |
1 2 3 4 | child left right parent |
关于@calfzhou的评论,您可以像往常一样使用
在线运行的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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) |
结果:
1 2 3 4 | A None B hello A1 6 B1 5 |
你也可以用它们来表示位置:
1 | B1(5, 6, b="hello", a=None) |
但是你必须记住MRO,它真的很让人困惑。
我可能有点烦人,但是我注意到人们每次重写一个方法时都忘记使用
另一个尚未涉及的点是传递初始化类的参数。由于
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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() |
给:
1 2 3 4 5 | [<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__ |
直接调用超类
总结:协作继承和用于初始化的超特定参数并不能很好地协同工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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() |
输出是
1 2 3 | first 10 second 20 that's it |
调用Third()查找在Third中定义的init。在那个例程中调用super调用First中定义的init。MRO =(一、二)。现在,调用第一个定义的init中的super将继续搜索MRO,并找到第二个定义的init,对super的任何调用都将命中缺省对象init。我希望这个例子能阐明这个概念。
如果你不先打电话给super。链停止,您将得到以下输出。
1 2 | first 10 that's it |
我想补充一下@Visionscaper在顶部说的话:
1 | Third --> First --> object --> Second --> object |
在这种情况下,解释器不会过滤掉对象类,因为它是重复的,而是因为Second出现在头部位置,而不是出现在层次结构子集的尾部位置。而对象只出现在尾部位置,在C3算法中不被认为是确定优先级的强位置。
类C (C)的线性化(mro)是
类C加上它的双亲P1 P2。= L(P1, P2,…它的父母的列表P1 P2 ..线性化合并是通过选择作为列表头部而不是尾部出现的公共类来完成的,因为顺序很重要(将在下面变得清楚)
第三个的线性化可以计算如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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" |
很明显,将如何解决这个方法
1 2 3 4 | 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" |
在学习pythonthehardway的过程中,我学到了一个叫做super()的东西,如果没有弄错的话,它是一个内置函数。调用super()函数可以帮助继承通过父类和"兄弟姐妹",并帮助您看得更清楚。我仍然是一个初学者,但是我喜欢分享我在python2.7中使用super()的经验。
如果您阅读了此页面的注释,您将听说方法解析顺序(Method Resolution Order, MRO),方法是您编写的函数,MRO将使用深度从左到右的模式进行搜索和运行。你可以做更多的研究。
通过添加super()函数
1 | super(First, self).__init__() #example for class First. |
通过在super()中添加每个实例和每一个实例,可以将多个实例和"家庭"与super()连接起来。它会执行这些方法,遍历它们确保你没有遗漏!然而,把它们加在前面或后面确实会有所不同,你会知道你是否做过学python的hardway练习44。让乐趣开始吧!
举个例子,你可以复制;粘贴并尝试运行它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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() |
它是如何运行的?fifth()的实例将是这样的。每个步骤从一个类到添加超函数的类。
1 2 3 4 | 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) |
家长找到了,还会继续往第三和第四!!
1 2 3 4 | 5.) print("third (before)") 6.) super()> First (Parent class) 7.) print ("Fourth (before)") 8.) super()> First (Parent class) |
现在已经访问了所有带有super()的类!父类已经找到并执行,现在它将继续在继承中解压函数以完成代码。
1 2 3 4 5 6 | 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 |
上述计划的结果:
1 2 3 4 5 6 7 8 9 | fifth (before) second (before third (before) fourth (before) first fourth (after) third (after) second (after) fifth (after) |
对于我来说,通过添加super(),我可以更清楚地看到python将如何执行我的代码,并确保继承可以访问我想要的方法。