pythonsuper函数_认识python中的super函数
需求分析
在類繼承中,存在這么一種情況:
class Human(object):
def Move(self):
print("我會(huì)走路...")
class Man(Human):
def Move(self):
print("我會(huì)跑步...")
man().Move()
輸出:
我會(huì)跑步...
人先會(huì)走路再會(huì)跑步的。如果你想調(diào)用Man().Move()時(shí)輸出以下要怎么做?
我會(huì)走路...
我會(huì)跑步...
這時(shí),其實(shí)便需要調(diào)用父類Human的Move()方法了。我們知道,在子類中,會(huì)覆父類的同名方法。即子類Man里的Move()方法將父類Human中的同名方法Move()覆蓋了。要想同時(shí)使得兩個(gè)方法都奏效,可以在子類中主動(dòng)調(diào)用父類中的Move()方法。其實(shí)很簡(jiǎn)單,在類Man中加點(diǎn)東西就行了,如下:
class Human(object):
def Move(self):
print("我會(huì)走路...")
class Man(Human):
def Move(self):
Human.Move(self)
print("我會(huì)跑步...")
Man().Move()
output:
我會(huì)走路...
我會(huì)跑步...
代碼中的Human.Move(self)便是在子類Man中主動(dòng)跟調(diào)用父類的方法。我來解釋一下這句代碼:
Human.Move(self)中Human是類,由類直接調(diào)用Move方法,這是未綁定的調(diào)用。
Human.Move(self)中的self其實(shí)是Man的一個(gè)實(shí)例Man(),一個(gè)個(gè)類的實(shí)例化是這樣的m=Man(),其中,m和Man()都是類Man的實(shí)例。
但是,這種方法有一個(gè)弊端。比如,如果父類Human吃飽沒事干給自己換個(gè)名字叫GoodHuman,那么麻煩就來了,你不僅要把Man(Human)改為Man(GoodHuman),還要把Human.Move(self)改為GoodHuman.Move(self)。
當(dāng)然,這里的例子改起來是沒什么可怕。但萬一在一些項(xiàng)目里像Human.Move(self)這類型的方法很多的話,你要一個(gè)一個(gè)改?
有沒有什么辦法,使得在父類更改名稱時(shí),子類只要改類似Man(Human)一處就好?
super()出場(chǎng)
super()的作用是:在類的繼承中,如果重定義某個(gè)方法,該方法會(huì)覆蓋父類的同名方法,但有時(shí),我們希望能同時(shí)實(shí)現(xiàn)父類的功能,這時(shí),我們就需要調(diào)用父類的方法了,可通過使用 super 來實(shí)現(xiàn)。
調(diào)用super()地常用格式是:
class C(B):
def meth(self, arg):
super(C, self).meth(arg)
現(xiàn)在,只要將代碼改為以下,就不怕父類名稱地變化了。(輸出結(jié)果和上述一樣)
class Human(object):
def Move(self):
print("我會(huì)走路...")
class Man(Human):
def Move(self):
super(Man, self).Move()
print("我會(huì)跑步...")
稍微深入
上面地例子很簡(jiǎn)單,因?yàn)轭怣an繼承地父類已有一個(gè)。當(dāng)繼承關(guān)系變復(fù)雜時(shí),(比如同時(shí)繼承很多個(gè)類,繼承的類又繼承其他類等等...),便要知道super()是怎么處理繼承順序了。
看這個(gè)例子:
class Base(object):
def __init__(self):
print("enter Base")
print("leave Base")
class A(Base):
def __init__(self):
print("enter A")
super(A, self).__init__()
print("leave A")
class B(Base):
def __init__(self):
print("enter B")
super(B, self).__init__()
print("leave B")
class C(A, B):
def __init__(self):
print("enter C")
super(C, self).__init__()
print("leave C")
按照前面的思路。我們意淫的輸出應(yīng)該是:
enter C
enter A
enter Base
leave Base
leave A
enter B
enter Base
leave Base
leave B
leave C
但實(shí)際輸出卻是:
enter C
enter A
enter B
enter Base
leave Base
leave B
leave A
leave C
我在這里提一個(gè)疑問:
**enter A的下一句為什么不是enter Base而是enter B么? **
揭開super的面紗
其實(shí),對(duì)于你定義的每一個(gè)類,Python 會(huì)用方法解析順序(Method Resolution Order, MRO)計(jì)算出一個(gè)列表,它代表了類繼承的順序,我們可以使用下面的方式獲得某個(gè)類的 MRO 列表:
>>>print(C.mro())
[, , , , ]
[Finished in 0.2s]
一個(gè)類的 MRO 列表就是合并所有父類的 MRO 列表,并遵循以下三條原則:
子類永遠(yuǎn)在父類前面;
如果有多個(gè)父類,會(huì)根據(jù)它們?cè)诹斜碇械捻樞虮粰z查;
如果對(duì)下一個(gè)類存在兩個(gè)合法的選擇,選擇第一個(gè)父類.
我們查看super函數(shù)的代碼:
ef super(cls, inst):
mro = inst.__class__.mro()
return mro[mro.index(cls) + 1]
其中,cls 代表類,inst 代表實(shí)例,上面的代碼做了兩件事:
獲取 inst 的 MRO 列表;
查找 cls 在當(dāng)前 MRO 列表中的 index, 并返回它的下一個(gè)類,即 mro[index + 1]。
當(dāng)你使用 super(cls, inst) 時(shí),Python 會(huì)在 inst 的 MRO 列表上搜索 cls 的下一個(gè)類。
現(xiàn)在我們來通過一步步追蹤來回答上面提出的答案:
做法很簡(jiǎn)單:
1, 修改一下代碼:
class Base(object):
def __init__(self):
print("enter Base")
print("leave Base")
print(self)
class A(Base):
def __init__(self):
print("enter A")
print(self)
super(A, self).__init__()
print("leave A")
class B(Base):
def __init__(self):
print("enter B")
print(self)
super(B, self).__init__()
print("leave B")
class C(A, B):
def __init__(self):
print("enter C")
print(self)
super(C, self).__init__()
print("leave C")
c = C()
print(hex(id(c)))
看輸出:
enter C
<__main__.C object at 0x000001C614CAB7F0>
enter A
<__main__.C object at 0x000001C614CAB7F0>
enter B
<__main__.C object at 0x000001C614CAB7F0>
enter Base
leave Base
<__main__.C object at 0x000001C614CAB7F0>
leave B
leave A
leave C
0x1c614cab7f0
在這里塞了這么多代碼,就為了說明兩件事:
在A,B,C,Base這四個(gè)類中,self都是<__main__.C object at 0x000001C614CAB7F0>這個(gè)對(duì)象。
<__main__.C object at 0x000001C614CAB7F0>是類C的實(shí)例c(c的id輸出是0x1c614cab7f0,說明它們是同樣的東西)。實(shí)例c也就是整個(gè)過程的發(fā)起者。
到了這里,就可以回答問題了:
首先看類C的__init__方法:
super(C, self).__init__()
這里的 self 是當(dāng)前 C 的實(shí)例,self.__class__.mro() 結(jié)果是:
[__main__.C, __main__.A, __main__.B, __main__.Base, object]
可以看到,C 的下一個(gè)類是 A,于是,跳到了 A 的 __init__,這時(shí)會(huì)打印出 enter A,并執(zhí)行下面一行代碼:
super(A, self).__init__()
注意,這里的 self 也是當(dāng)前 C 的實(shí)例,MRO 列表跟上面是一樣的,搜索 A 在 MRO 中的下一個(gè)類,發(fā)現(xiàn)是 B,于是,跳到了 B 的__init__,這時(shí)會(huì)打印出enter B,而不是enter Base。
總結(jié)
super和父類沒有實(shí)質(zhì)性的關(guān)聯(lián)。通過上面分析。我們知道super函數(shù)是根據(jù)self以及該self對(duì)應(yīng)的方法解析順序mro來工作的。
再強(qiáng)調(diào)一下,self是一個(gè)實(shí)例對(duì)象,在這里就是實(shí)例c(上面代碼有一句c=C()),整個(gè)過程的發(fā)起者。
版權(quán):保留所有權(quán),轉(zhuǎn)載請(qǐng)注明出處!
總結(jié)
以上是生活随笔為你收集整理的pythonsuper函数_认识python中的super函数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python哪些模块用于数据分析_pyt
- 下一篇: python3.7怎么设置中文_解决 B