深入解析Python中super()方法的调用机制与MRO原理
本文深入剖析Python中super()方法的底层原理,重点讲解C3算法生成的MRO(方法解析顺序)列表。通过对比Python 2与Python 3的新式类差异,结合钻石继承案例,阐明广度优先调用机制及子类实例传递逻辑,帮助开发者准确掌握多继承中的方法调用顺序。
一、前言
在Python面向对象编程中,继承是一个核心概念。初学者往往觉得它功能强大,其中涉及的super类也频繁出现。近期通过实际编码练习,我才真正理清了它的运作逻辑。特此整理分享,供后续研究者参考,毕竟如今编程教育已日益普及。
二、代码示例与分析
直接展示核心代码,若能正确回答下方问题,即可跳过后续详解。
class A():
def go(self):
print ("go A go!")
def stop(self):
print ("stop A stop!")
def pause(self):
raise Exception("Not Implemented")
class B(A):
def go(self):
super(B, self).go()
print ("go B go!")
class C(A):
def go(self):
super(C, self).go()
print ("go C go!")
def stop(self):
super(C, self).stop()
print ("stop C stop!")
class D(B,C):
def go(self):
super(D, self).go()
print ("go D go!")
def stop(self):
super(D, self).stop()
print ("stop D stop!")
def pause(self):
print ("wait D wait!")
class E(B,C):
pass
a = A()
b = B()
c = C()
d = D()
e = E()
# 说明下列代码的输出结果
a.go()
print('--------')
b.go()
print('--------')
c.go()
print('--------')
d.go()
print('--------')
e.go()
print('--------')
a.stop()
print('--------')
b.stop()
print('--------')
c.stop()
print('--------')
d.stop()
print('--------')
e.stop()
print(D.mro())
a.pause()
b.pause()
c.pause()
d.pause()
e.pause()
虽然直接运行代码可得结果,但建议先自行推导。初次看到执行结果时,其输出顺序可能令人意外。
step1:
需明确几个基础概念:
继承的主要功能:实现父类代码的复用。
多态的功能:同一方法针对不同对象类型产生相应不同的结果。
开闭原则:软件实体应对扩展开放,对修改封闭。
super类的功能:在新式类中实现广度优先且不重复的父类调用,从而解决钻石继承(多继承)带来的难题。
step2:
super的实现原理:基于C3算法生成MRO(Method Resolution Order,方法解析顺序)列表,依据该列表中元素的顺序进行查询和调用。
新式类的调用顺序为广度优先,而旧式类为深度优先。
step3:
个人理解要点:
1. 调用父类方法时,传入的是子类的实例对象。
2. 在新式类子类(如A, B)中,A出现在B之前。
3. super类似一种嵌套设计:当代码执行到super实例化后,先查找同级父类;若无其他父类,则执行自身父类,再向下继续。
简洁概括为三个原则:
子类在父类之前列出,所有类不重复调用,顺序从左到右。
理解上述规则后,相关题目即可迎刃而解。
无需再运行代码,答案如下:
a.go()# go A go! b.go()# go A go!# go B go! c.go()# go A go!# go C go! d.go()# go A go!# go C go!# go B go!# go D go! e.go()# go A go!# go C go!# go B go! a.stop()# stop A stop! b.stop()# stop A stop! c.stop()# stop A stop!# stop C stop! d.stop()# stop A stop!# stop C stop!# stop D stop! e.stop()# stop A stop! a.pause()# ... Exception: Not Implemented b.pause()# ... Exception: Not Implemented c.pause()# ... Exception: Not Implemented d.pause()# wait D wait! e.pause()# ...Exception: Not Implemented
观察答案后,还有一个细节值得注意:若父类抛出异常,而子类有未抛出异常的方法,则该异常不会被抛出。这一设计在实际应用中颇具价值。
此处中间A,C,B,D的顺序与网上常见示例略有不同,这促使我深入研究,主要对应个人理解中的第三条。
补充说明:
Python 2与Python 3在此问题上的差异
Python 2默认不继承object类。
Python 3默认所有类均继承object类,均为新式类。
Python 2中super调用方式为:super(开始类名,self).函数名()。
Python 3中super调用简化为:super().函数名()。
关于调用父类函数时传入子类实例的示例如下:
class A:
def __init__(self):
self.n = 2
def add(self, m):
print('self is {0} @A.add'.format(self))
self.n += m
class B(A):
def __init__(self):
self.n = 3
def add(self, m):
print('self is {0} @B.add'.format(self))
super().add(m)
print('newb')
self.n += 3
class C(A):
def __init__(self):
self.n = 4
def add(self, m):
print('self is {0} @C.add'.format(self))
super().add(m)
print('newc')
self.n += 4
class D(B, C):
def __init__(self):
self.n = 5
def add(self, m):
print('self is {0} @D.add'.format(self))
super().add(m)
self.n += 5
d = D()
d.add(2)
print(d.n)
总结
以上介绍了Python中super()的用法和原理,希望对大家有所帮助。如有疑问请留言,小编会及时回复。感谢大家对本站的支持!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









