Python C3-MRO算法解析:多重继承下的方法解析顺序详解
Python中多继承C3-MRO算法的剖析
方法解析顺序(MethodResolutionOrder,MRO)是面向对象编程中的一个关键概念,特别是在多重继承的情况下。它确定查找实例对象的属性和方法的顺序。
本文旨在深入分析Python中的MRO生成算法,特别是C3-MRO算法的逻辑和实现。
1.**概念分析**理解MRO的基础是知道它是如何决定方法在类层次结构中的搜索顺序的。
单继承中,MRO比较简单;在多重继承中,情况变得复杂,尤其是当类层次结构混乱时。
2.**C3-MRO算法**C3-MRO算法是用于创建多继承MRO的核心排序算法。
该算法首次在1996年OOPSLA会议上提出,并在Python2.3版本中用于新型类的MRO分析。
其核心逻辑是确保子类保持单调性并遵循本地优先级高于父类。
3.**算法核心**C3-MRO算法的主要公式清晰无歧义,MRO是通过递归调用和序列合并操作生成的。
合并顺序合并操作决定了类在继承层次结构中的顺序。
递归过程确保子类先于父类处理,从而构建合理的MRO。
4.**算法应用**理解C3-MRO算法的核心在于其排序逻辑。
该算法在保证子类优先级的同时保持单调性,并在处理遗留分支时做出明智的决策,以确保MRO的稳定性和一致性。
5.**实现代码**要实现C3-MRO算法,您需要了解递归调用和序列合并的概念。
特定于实现的代码通常涉及类的递归遍历和合并多个继承路径的逻辑。
6.**问题与思考**最后给出两个关于面向对象设计的开放式问题,鼓励读者思考和讨论,特别是如何正确调用文中的方法的魔鬼三角形继承问题和恐怖菱形继承问题。
。
文章最后指出,虽然本文主要关注Python中的MRO分析及其C3-MRO算法,但可以通过指定渠道获取相关资源和学习资料,鼓励读者探索更多深奥。
面向对象编程的一个方面。
Python继承可以继承多少次
绕过如下父类:代码如下:
classA():
deffoo1(self):
print“A”
classB(A):
deffoo2(self):
pass
classC(A):
deffoo1(self):
打印“c”classD(B,C):
通过
d=D()
d.foo1()
按照经典的类搜索顺序,根据从左到右的深度优先规则,访问d.foo1()时,类D不存在。
然后先搜索,找到B。
首先访问A,找到foo1(),所以这次调用了A的foo1,导致foo1被C重写,绕过了at()。
Python引入了新式类的概念。
每个基类都继承自Object,并且其匹配规则也预先更改为C3。
C3算法
C3算法是如何匹配的...跟随Q&A中的讨论部分,可以总结如下:
C3算法的基本合并。
在合并列表中,如果第一个序列mro是第一个排名,则出现在其他序列列,并且第一个也在,或者没有其他序列可见,那么这个类将从这些序列中删除并合并到访问序列列表中
例如:(在问题中引用庄则博的回答@zhuangzebo)
代码如下:
classA(O):pass
classB(O):pass
classC(O)):通过
OrbitD(A,B):pass
Orbit(C,D):Pass
首先你需要知道O(的mroobject)(methodResolutionOrder)列表为[O,]
那么下一步就是:
代码如下:
mro(A)=[A,O]
mro(b)=[B,O]
mro(C)=[C,O]
mro(D)=[D]+merge(mro(A),mro(B),[A,B])
=[D]+合并([A,O],[B,O],[A,B])
=[D,A]+合并([o],[b,o],[b])
=[d,a,b]+合并([o],[o])
=[D,A,B,O]
mro(E)=[E]+合并e(mro(C),mro(D),[C,D])
=[E]+merge([C,O],[D,A,B,O],[C,d])
=[e,c]+合并([o],[d,a,b,o],[d])
=[e,c,d]+合并([o],[a,b,o])</p>
=[e,c,d,a,b]+mer([o],[o])
=[e,c,d,a,b,o]
那么还有一种特殊情况:
例如:
merge(DO,CO,C)第一个mergeD
merge(DO,C)是O,c)最先合并的是c
意思是即当一个正方形出现在两个序列的顶部(如C)时,这种情况与该正方形仅出现在一个序列的顶部(如D)时这些情况同时出现的情况不同如果出现,则按顺序进行匹配。
新样式类生成的访问序列存储在名为MRO的只读列表中。
您可以使用instance.__mro__或instance.mro()来访问Cando。
/p>
匹配结束时,按照MRO序列的顺序进行匹配
C3和宽度优先级的区别:
示例这样就完全清楚了:
代码如下:
ClassA(Object):pass
ClassB(A):pass
优雅(B):通过
ClassD(A):通过
ClassE(D):通过
ClassF(C,E):通过
宽度-根据第一次遍历,F的MRO序列应该是[F,C,E,B,D,A]
但是C3是[F,E,D,C,B,A]
>这意味着您可以将其用作C3就是将一个链接深度交叉到与另一个链接的交集,然后再深度交叉另一个链接,最后交叉交集有新样式类和经典类通过类名访问查询。
经典类,如果要访问父类,就使用类名来访问。
代码如下:
classA():
def__init__(self):
打印“A”
classB(A):
def__init__(self):
打印"B"
A.__init__(self)#Python默认不会调用父类的初始化函数
看来是这样不是问题,但如果使用...将是一个问题
因此建议要么始终使用super,要么始终使用通过类名访问
最佳实现:
避免多重继承
一致使用super
不要混合经典类和新式类
调用父类时注意检查时间类层次结构请注意
以上是我对Python类继承的理解,希望能对大家有所帮助
理解Python中的多重继承?
9.5.1。
多重继承
Python对多重继承形式的支持也很有限。
多重继承的类定义如下例所示:
classDerivedClassName(Base1,Base2,Base3):
大多数情况下,在最简单的情况下,您要查找的搜索人们可以认为属性是从父类深度优先继承的,从左到右,而不是在存在重叠的同一类层次结构中找到它们两次。
因此,如果在DerivedClassName(示例中的派生类)中未找到属性,则搜索Base1,然后(递归地)搜索其基类,如果未找到,则搜索Base2,依此类推。
事实上,super()可以动态改变解析顺序。
这个方法在其他一些多重继承语言中也能看到,类似于call-next-method,在单继承语言中比super更强大。
动态调整顺序是必要的,因为所有的多重继承都会有一个或多个钻石关系(意味着至少有一个祖先类可以通过多重继承路径子类到达)。
例如,所有新样式类继承自对象,因此任何多重继承都将始终有多个对象的继承路径。
为了防止重复访问基类,通过动态线性化算法,每个类都有专门指定的从左到右的顺序,每个祖先类只被访问一次,称为单调的。
类被继承而不影响其祖先的顺序)。
最后,通过这种方式可以设计出一个可靠且可扩展的多重继承类。
更多信息请参见
Python支持多重继承。
和C++一样,会出现一个问题:子类继承的多个基类都继承同一个基类。
有可能多次调用父类构造方法。
关于这个问题,我查到了一些资料,虽然我自己没有验证过,这里我总结一下我对这个问题的想法。
Python和C++对于这个问题有不同的解决方案,当然Python也取决于它的版本。
C++采用的解决方案是引入虚拟继承的语法,以防止多次创建同一个类。
Python使用的方法是MRO(methodresolutionorder)。
Python2.3之前,MRO的实现是基于DFS的,Python2.3之后,MRO的实现是基于C3算法的。
找到的信息解释了改变算法的原因:
为什么使用C3算法
C3算法首先是为Lisp提出的。
是用Python开发和实现的,以解决基本的基于深度的问题。
优先级搜索算法不能满足局部优先级和单调性问题。
局部优先级:指声明时父类的顺序,如C(A,B)。
如果访问C类对象的属性,应该先搜索A类,然后按照声明的顺序搜索B类。
单调性:如果在C中的解析顺序中,A排在B之前,那么在C的所有子类中也必须满足这个顺序。
--------------------------------新功能发现的顺序-样式类与旧式的类不同----------------------------------
在新式中style类,当调用对象的find函数或属性时,会执行广度优先搜索。
在旧式课程中,首先进行深度探索。
结论:以上所有材料都是主CTO给出的关于Python继承可以继承多少次的内容。
我希望它对每个人都有用。
如果您想了解更多有关此信息。
记得收藏并关注本站。
Python的super函数直观理解
直观理解super函数super函数的主要作用是调用父类的方法。简单来说,当你在子类中使用super()时,它会帮你执行父类中相应的函数。
例如,在单继承中,当你在子类B中使用super().p()时,这实际上调用了父类A中的p方法。
在复杂的情况下,比如多重继承,理解super的使用需要认识MRO(方法解决方案顺序)。
MRO描述了类的继承顺序,有助于理解super调用如何在不同类之间分布。
您可以通过查看类的__mro__属性来获取MRO。
以A类和C类为例,A类的MRO为[A,object],C类的MRO为[C,A,B,object]。
super()返回当前对象的MRO对应的父类。
例如,super(C,d)返回A,因此super(C,d).p()将调用A上的方法p。
类似地,super(B,d)返回A,因此super(B,d).p()会调用B中的方法p。
当类中没有super调用时,根据MRO确定结果。
如果类D的MRO是(D,C,B,A,object),super()将返回C并在C上执行方法p,而不调用A或B的方法。
对于多重继承,使用super的含义是类似于单一继承。
关键在于MRO,它决定了执行的顺序。
查看MRO,可以清楚地了解超级调用如何在不同类之间分配。
总结:理解super函数的关键是要知道当前对象的MRO,找到调用的父类。
使用super()函数时,必须注意MRO,以保证父类方法被正确调用。
如果有疑问,可以通过实际代码测试直观地了解super的使用。