python的super函数学习
作者:@skyflask
转载本文请注明出处:https://www.cnblogs.com/skyflask/p/7701671.html
目录
一、为什么要用super?
二、新式类和老式类(经典类)区别
三、老式方法(unbound方法)和super方法的差异
四、The Python 2.3 MRO(Method Resolution Order)
五、总结
一、为什么要用super?
在Python 2.2以前,通常的做法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class A: def __init__( self ): print "enter A" print "leave A" class B(A): def __init__( self ): print "enter B" A.__init__( self ) print "leave B" >>> b = B() enter B enter A leave A leave B |
问题出现!
使用非绑定的类方法(用类名来引用的方法)来调用,并在参数列表中,引入待绑定的对象(self),从而达到调用父类的目的。
但是,问题来了!当父类发生变化时(如类B的父类由A变为C时),必须遍历整个类定义,把所有的通过非绑定的方法的类名全部替换过来。
1 2 3 4 5 | class B(C): # A --> C def __init__( self ): print "enter B" C.__init__( self ) # A --> C print "leave B" |
问题解决!-引入super
如果代码少,还改得过来,但是一旦代码上万行,就需要慢慢修改了。因此,自Python 2.2开始,Python添加了一个关键字super,来解决这个问题。下面是Python 2.3的官方文档说明:
1 2 3 4 5 6 7 8 9 10 | super ( type [, object - or - type ]) Return the superclass of type . If the second argument is omitted the super object returned is unbound. If the second argument is an object , isinstance (obj, type ) must be true. If the second argument is a type , issubclass (type2, type ) must be true. super () only works for new - style classes. A typical use for calling a cooperative superclass method is : class C(B): def meth( self , arg): super (C, self ).meth(arg) New in version 2.2 . |
所以,上面的代码我们可以改为如下:
1 2 3 4 5 6 7 8 9 | class A( object ): # A must be new-style class def __init__( self ): print "enter A" print "leave A" class B(C): # A --> C def __init__( self ): print "enter B" super (B, self ).__init__() print "leave B" |
这样,就只要改继承的类名和基类的object两个地方。
用super的好处是,可以不用直接引用基类的名称就可以调用基类的方法。如果我们改变了基类的名称,那么所有子类的调用将不用改变。
二、新式类和老式类(经典类)区别
从上面可以看出,使用super是从2.2开始,且需要是新式类才支持。新式类和经典类区别如下:
- 新式类都从object继承,经典类不需要。
- 新式类的MRO(method resolution order 基类搜索顺序)算法采用C3算法广度优先搜索,而旧式类的MRO算法是采用深度优先搜索。
- 新式类相同父类只执行一次构造函数,经典类重复执行多次。
- 新式类的object基类是type类型,经典类的基类是classobj类型。
如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import inspect class A: #改为(object)为新式样类 def __init__( self ): print 'A' print type (A) class B(A): def __init__( self ): print 'B' A.__init__( self ) for x in inspect.getmro(B): print dir (x) b = B() |
经典类结果为:
1 2 3 4 5 6 | [root@zabbix src] # python tes4.py [ '__doc__' , '__init__' , '__module__' ] [ '__doc__' , '__init__' , '__module__' ] B A < type 'classobj' > |
新式类结果为:
1 2 3 4 5 6 | [ '__class__' , '__delattr__' , '__dict__' , '__doc__' , '__format__' , '__getattribute__' , '__hash__' , '__init__' , '__module__' , '__new__' , '__reduce__' , '__reduce_ex__' , '__repr__' , '__setattr__' , '__sizeof__' , '__str__' , '__subclasshook__' , '__weakref__' ] [ '__class__' , '__delattr__' , '__dict__' , '__doc__' , '__format__' , '__getattribute__' , '__hash__' , '__init__' , '__module__' , '__new__' , '__reduce__' , '__reduce_ex__' , '__repr__' , '__setattr__' , '__sizeof__' , '__str__' , '__subclasshook__' , '__weakref__' ] [ '__class__' , '__delattr__' , '__doc__' , '__format__' , '__getattribute__' , '__hash__' , '__init__' , '__new__' , '__reduce__' , '__reduce_ex__' , '__repr__' , '__setattr__' , '__sizeof__' , '__str__' , '__subclasshook__' ] B A < type 'type' > |
三、老式方法(unbound方法)和super方法的差异
老式方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class A( object ): def __init__( self ): print "enter A" print "leave A" class B(A): def __init__( self ): print "enter B" A.__init__( self ) print "leave B" class C(A): def __init__( self ): print "enter C" A.__init__( self ) print "leave C" class D(B,C): def __init__( self ): print "enter D" B.__init__( self ) C.__init__( self ) print "leave D" |
结构如下:
1 2 3 4 5 6 7 8 9 10 11 | >>> d = D() enter D enter B enter A leave A leave B enter C enter A leave A leave C leave D |
super方法调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class A( object ): def __init__( self ): print "enter A" print "leave A" class B(A): def __init__( self ): print "enter B" super (B, self ).__init__() print "leave B" class C(A): def __init__( self ): print "enter C" super (C, self ).__init__() print "leave C" class D(B,C): def __init__( self ): print "enter D" super (D, self ).__init__() print "leave D" |
super方法结果如下:
1 2 3 4 5 6 7 8 9 | >>> d = D() enter D enter B enter C enter A leave A leave C leave B leave D |
从老式方法的结果可以发现, 这里面A的初始化函数被执行了两次. 因为我们同时要实现B和C的初始化函数, 所以分开调用两次, 这是必然的结果.
从super方法的结果会发现,所有父类ABC只执行了一次, 并不像之前那样执行了两次A的初始化.
然后, 又发现一个很奇怪的: 父类的执行是 BCA 的顺序并且是全进入后再统一出去. 这是MRO表问题
四、The Python 2.3 MRO(Method Resolution Order)
参考文章:https://www.python.org/download/releases/2.3/mro/
1. 如果是经典类MRO为DFS(深度优先搜索(子节点顺序:从下到上,从左到右))。
2. 如果是新式类MRO为BFS(广度优先搜索(子节点顺序:从下到上,从左到右))。
五、总结
1. super并不是一个函数,是一个类名,super(B, self)事实上返回是的一个super对象;
2. super(B, self).func的调用并不是用于调用当前类的父类的func函数;
3. Python的多继承类是通过mro的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数只调用一次(如果每个类都使用super,确保没A.func);
4.如果类被设计成使用了super,那么所有子类也必须要调用super,否则直接调用会出现重复调用的问题
5.super不是简单地调用基类的方法,而是调用MRO中的下一个类的方法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」