python super方法,Python super()使用注意事项
很高大上的函数,如果在python的manual 13里用多了,可以让你看起来像个高手23333。但其实还是很重要的。简单来说,超级函数就是调用下一个父类(超类)并返回父类实例的方法。这里的下一个概念是在后面的MRO表中介绍的。
帮助介绍如下:
超级(type,obj)绑定的超级对象;需要isinstance(obj,type)
超级(类型)-未绑定的超级对象
超(type,type2)绑定的超对象;需要issubclass(类型2,类型)
调用协作超类方法的典型用法:
丙类(乙类):
定义方法(self,arg):
超级(C,自我)。meth(arg)。因此,super有三种用法。第一个参数总是调用父类的类,第二个参数是可选的(返回未绑定的父类对象)。它也可以是该类的实例对象或子类。最终返回父类的所有实例(绑定或未绑定)。在Python3中,super函数的另一种用法是direct super(),相当于super(type,first parameter)。第一个参数是一般的传入self实例本身,因为它通常也用py2编写。
另外,在py2中,super只支持新类(new-style class,继承自object)。
为什么要调用父类?当一个类继承时,如果一个方法被重定义,它将覆盖与父类同名的相应方法。通过调用父类的实例,父类的功能可以同时在子类中实现。例如:
#应该是基于python2中对象的新类。
A类(对象):
def __init__(self):
打印“输入A”
打印“留下一个”
B(A)类:
def __init__(self):
打印输入B
超级(B,自我)。__init__()
打印“离开B”
b=B()
输入B
输入A
留下一个
Leave B可以通过调用super实现父类实例的初始化功能。这是实践中经常用到的(因为需要继承父类的功能,又有新的功能)。
使用父类直接调用的区别其实上面的超函数方法也可以这样写:
A类(对象):
def __init__(self):
打印“输入A”
打印“留下一个”
B(A)类:
def __init__(self):
打印输入B
A.__init__(self)
Print leave B 直接用父类名调用父类其实是可行的。至少在上面的例子中,现在效果是一样的。这个方法也是调用老类中父类的唯一方法(老类没有super)。
通过父类名调用方法是非常常见和直观的。但是,它的效果和超级不一样。例如:
A类(对象):
def __init__(self):
打印“输入A”
打印“留下一个”
B(A)类:
def __init__(self):
打印输入B
A.__init__(self)
打印“离开B”
丙类(甲类):
def __init__(self):
打印输入C
A.__init__(self)
打印“左C”
D类(B、C类):
def __init__(self):
打印输入D
B.__init__(self)
C.__init__(self)
打印“离开D”
d=D()
输入D
输入B
输入A
留下一个
离开B
输入C
输入A
留下一个
离开C
离开D可以发现A的初始化函数被执行了两次。因为要同时实现B和C的初始化函数,所以分别调用两次是必然的结果。
但是如果改写成super呢?
A类(对象):
def __init__(self):
打印“输入A”
打印“留下一个”
B(A)类:
def __init__(self):
打印输入B
超级(B,自我)。__init__()
打印“离开B”
丙类(甲类):
def __init__(self):
打印输入C
超级(C,自我)。__init__()
打印“左C”
D类(B、C类):
def __init__(self):
打印输入D
超级(D,自我)。__init__()
打印“离开D”
d=D()
输入D
输入B
输入C
输入A
留下一个
离开C
离开B
离开D会发现所有的父类ABC都只执行一次,而不是像以前一样初始化A两次。
然后,一个奇怪的事情被发现了:父类的执行是BCA的命令,而且都是输入然后统一的。这就是MRO表问题,后面会讨论。
如果没有多重继承,super其实类似于通过父类调用方法。但是,super还有一个好处:当B继承A,写成A.__init__,如果根据需要把所有的重构都改成继承E,那么都得重新改一遍!这很麻烦,容易出错!但是使用super()不需要一个一个改(在类定义里改就行了)。反正你可以发现超级没那么简单。
MRO餐桌上的MRO是什么?可以通过以下方式转出:
D.mro() #或d.__class__。mro()或D.__class__。mro(D)
[D,B,C,A,object]
磁共振成像技术
[B,A,对象]
帮助(维修)
#文档字符串:
#mro() -列表
#返回类型的方法解析顺序
#Type: method_descriptorMRO是一个类的方法解析序列表,实际上是继承父类方法时的序列表(类继承序列表来理解它)。
这块表有什么用?首先,找出真正的管理员做了什么:
高级定义(cls,inst):
mro=inst。__class__。mro()
Return mro[mro.index(cls) 1]换句话说,super方法实际上调用了cls的mro表中的下一个类。如果是简单的一行单继承,那就是父类——父类一个一个往下。但是对于多重继承,应该遵循MRO表中的顺序。以上面D的调用为例:
数据的初始化
-D(回车D) super(D,self)
-父类B(输入B)超级(B,自身)
-父类C (Enter C)超级(C,self)
-Parent父类A(输入A)(退出A) #如果是,继续超(A,self)-object(停止)
-(出口C)
-(B出口)
-(退出D)所以,MRO表中的超类初始化函数只执行一次!
那么,MRO的秩序是什么?这可以参考官方指令Python 2.3方法解析顺序。基本上就是计算每个类的MRO(从父类到子类的顺序),合并成一条线。请遵循以下规则:
在MRO中,基类总是出现在派生类之后。如果有多个基类,基类的相对顺序保持不变。这个原则包括两点:
当类被定义影响相对顺序时,基类的继承顺序总是在派生类之后。
那么MRO就是:F-E-B-C-D-A-Object
怎么解释呢?
按照官方的方法,是:
L(O)=O
L(B)=B O
L(A)=A O
L(C)=C A O
L(D)=D A O
L(E)=E merge(L(B),L(C))
=E合并(BO,CAO)
=E B merge(O,CAO)
=E B C merge(O,AO)
=E B C A merge(O,O)
=欧洲银行
L(F)=F merge(L(E),L(D))
=F合并(EBCAO,DAO)
=F EBC合并(AO,DAO)
=F EBC D合并(AO,AO)
=F EBC D AO看起来很复杂.但在MRO仍然如此。基类总是出现在派生类之后。如果有多个基类,基类的相对顺序保持不变。所以,就我个人而言,我认为我可以这样想:
首先找到最长最深的继承路径F- E- C- A- object。(因为基类总是出现在派生类之后是必然的。)相似深度先设置休止符顺序:F- E- B- obj
,F- D- A-object如果有多个基类,基类的相对顺序会保持不变,类似于合并时提前的项。所以安排这些路线:(FEBO,FECAO,FDAO)F- E- B- obj
而E(B,C)决定B在C之前,所以F- E- B- C- A- obj
(相当于F合并(EBO,ECAO)
).一个物体
而F(E,D)确定D在E之后,所以D在E之后a之前,因为相对顺序,所以等价于FE归并(BCAO,道)
,所以FE BC D AO
这是一个超级课堂。当我们调用super()时,我们实际上实例化了一个超类。你没看错。super是一个类,既不是关键字,也不是函数等其他数据结构:
A类:通过
.
s=超级(A)
类型
在大多数情况下,super包含两条非常重要的信息:一个MRO和一个MRO的类。如下调用super时:
Super(a_type,obj)MRO指的是(obj)类型的MRO,MRO的那个类是a_type,而is instance(obj,A _ type)==true。
这样称呼的时候:
Super(type1,type2)MRO指的是type2的MRO,MRO的类是type1,issubclass(type2,type1)==True。
那么,super()实际上做了什么呢?简单来说:在MRO中提供一个MRO和一个类C,super()会返回一个对象,从MRO中C之后的类中查找方法。
也就是说,查找方式时不是像常规方法一样从所有的大修类中查找,而是从大修的尾巴中查找。
举个栗子,有个MRO:
[甲、乙、丙、丁、戊、对象]下面的调用:
超级(丙,甲)款.foo()超级只会从C之后查找,即:只会在D或E或目标中查找富(中国姓氏)方法。
多继承中极好的的工作方式再回到前面的
d=D()
d.add(2)
打印(数据)现在你可能已经有点眉目,为什么输出会是
自我是__main__ .位于0x10ce10e48 @D.add的d对象
自我是__main__ .位于0x10ce10e48 @B的d对象。添加
自我是__main__ .位于0x10ce10e48 @C.add的d对象
自我是__main__ .位于0x10ce10e48 @A.add的d对象
19下面我们来具体分析一下:
D的大修是:[D,B,C,A,object]。备注:可以通过D.mro() (Python 2使用D.__mro__)来查看D的大修信息)详细的代码分析如下:A类:
def __init__(self):
self.n=2
定义添加(自身,m):
# 第四步
# 来自d。添加中的极好的
# self==d,self.n==d.n==5
print(self is {0} @A.add .格式(自身))
self.n=m
# d.n==7
乙(甲)类:
def __init__(self):
self.n=3
定义添加(自身,m):
# 第二步
# 来自d。添加中的极好的
# self==d,self.n==d.n==5
打印(自己是{0} @B.add .格式(自身))
# 等价于suepr(B,self).添加(m)
#自我的大修是[D,B,C,A,object]
# 从B之后的【丙、甲、对象]中查找增加方法
超级()。添加(m)
# 第六步
#数据=11
self.n=3
# d.n=14
丙类(甲类):
def __init__(self):
self.n=4
定义添加(自身,m):
# 第三步
# 来自乙。添加中的极好的
# self==d,self.n==d.n==5
print(self is {0} @C.add .格式(自身))
# 等价于suepr(C,self).添加(m)
#自我的大修是[D,B,C,A,object]
# 从C之后的【答,对象]中查找增加方法
超级()。添加(m)
# 第五步
# d.n=7
self.n=4
#数据=11
D类(B、C类):
def __init__(self):
self.n=5
定义添加(自身,m):
# 第一步
print(self is {0} @D.add .格式(自身))
# 等价于超级(四、自我)。添加(m)
#自我的大修是[D,B,C,A,object]
# 从D之后的[乙,丙,甲,对象]中查找增加方法
超级()。添加(m)
# 第七步
# d.n=14
self.n=5
# self.n=19
d=D()
d.add(2)
打印(数据)调用过程图如下:
D.mro()==[D,B,C,A,object]
d=D()
d.n==5
d.add(2)
D类(乙、丙):乙类(甲):丙类答:答类:
def add(self,m): def add(self,m): def add(self,m): def add(self,m):
超级()。添加(m) 1 .-超级()。添加(m) 2 .-超级()。添加(m) 3 .- self.n=m
self.n=5 - 6 .self.n=3 - 5 .self.n=4 - 4 .-
(14 5=19) (11 3=14) (7 4=11) (5 2=7)
现在你知道为什么d.add(2)后域名的值是19 了吧;)
参考参考1
参考2
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。