小編給大家分享一下Python中super().__init__和Base.__init__的區(qū)別是什么,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
德令哈網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)建站!從網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、APP開(kāi)發(fā)、成都響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開(kāi)發(fā),運(yùn)營(yíng)維護(hù)。創(chuàng)新互聯(lián)建站自2013年起到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來(lái)保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)建站。
我們?cè)谑褂胮ython中的類繼承時(shí),子類繼承父類后,在重載父類的方法后,在方法中如果要執(zhí)行父類對(duì)應(yīng)的方法,一般有兩種方式:super和Base(表示父類名)。
使用例子
先看下面一段代碼:
# -*- coding: utf-8 -*-class Base: def __init__(self): self.postion = (0, 0) def move(self, x, y): self.postion = (self.postion[0] + x, self.postion[1] + y)class Device(Base): def __init__(self): super(Device, self).__init__() self.offset = (0, 0) # 記錄本次位置偏移量 def move(self, x, y): self.offset = (self.postion[0] - x, self.postion[1] - y) super(Device, self).move(x, y) def get_offset(self): return self.offset
在Base類中,有一個(gè)move方法用來(lái)移動(dòng)位置,在子類中我們想要增加一個(gè)記錄,記住每次移動(dòng)的偏移量,那么我們就在子類中重載move函數(shù),執(zhí)行我們自定義的操作(記錄偏移量),然后繼續(xù)執(zhí)行父類的move動(dòng)作,直接調(diào)用父類move函數(shù)就可以避免我們重新實(shí)現(xiàn)移動(dòng)位置的動(dòng)作。
在上面的例子中,我們使用了super來(lái)調(diào)用父類方法,那么能不能使用Base來(lái)調(diào)用呢?
.... Base.__init__(self) ...... Base.move(self, x, y) ....
可以看到Base調(diào)用父類函數(shù)時(shí),必須在函數(shù)中傳遞self參數(shù)。之前的文章中我們了解到類的普通函數(shù)調(diào)用需要使用類對(duì)象調(diào)用,而類的普通函數(shù)第一個(gè)參數(shù)默認(rèn)是self,調(diào)用時(shí)不需要傳遞此參數(shù),因?yàn)橥ㄟ^(guò)對(duì)象調(diào)用會(huì)自動(dòng)傳遞。但是直接使用Base類名調(diào)用時(shí),方法內(nèi)部需要知道self是誰(shuí)。那么兩種方式都可以,他們有區(qū)別嗎?當(dāng)然是有的
首先看一下super的定義
class super(object): """ super() -> same as super(__class__,) super(type) -> unbound super object super(type, obj) -> bound super object; requires isinstance(obj, type) super(type, type2) -> bound super object; requires issubclass(type2, type) Typical use to call a cooperative superclass method: class C(B): def meth(self, arg): super().meth(arg) This works for class methods too: class C(B): @classmethod def cmeth(cls, arg): super().cmeth(arg) """
可以看到,super有四種調(diào)用方式
super(): 相當(dāng)于super(當(dāng)前類名, 第一個(gè)參數(shù)) python3中新增的方式
super(type):沒(méi)有綁定對(duì)象
super(type, obj):綁定對(duì)象,要求obj的類型是type或者其子類
super(type, type2):綁定對(duì)象,要求type2是type的子類
這里我們就先說(shuō)一下super()和super(type, obj),這是我們常用的方式
在上面的例子中我們看到super和Base的方式一樣,接下來(lái)我們?cè)倏匆粋€(gè)例子
# -*- coding: utf-8 -*-class Base: def __init__(self): print("Base") self.name = "Base"class Device1(Base): def __init__(self): Base.__init__(self) print("Device1") self.name1 = "Device1"class Device2(Base): def __init__(self): Base.__init__(self) print("Device2") self.name2 = "Device2"class Sub(Device1, Device2): def __init__(self): Device1.__init__(self) Device2.__init__(self) print("Sub") self.name3 = "Sub" def test(self): print("test: ", self.name2) s = Sub() s.test()# 輸出:Base #Base.__init__中的printDevice1 #Device1.__init__中的printBase #Base.__init__中的printDevice2 #Device2.__init__中的printSub #Sub.__init__中的printtest: Device2 #test方法中的print
四個(gè)類,Base初始化函數(shù)被調(diào)用了兩次,為什么呢?Sub.__init__中Device1和Device2都調(diào)用了初始化方法,是這個(gè)原因嗎?準(zhǔn)確點(diǎn)來(lái)講,是的,可不可以只調(diào)用一個(gè),那么Base就只會(huì)被調(diào)用一次嘍,如果只調(diào)用Device1.__init__會(huì)有什么結(jié)果?
Base Device1 Sub Traceback (most recent call last): File "/Users/small_bud/Desktop/Python/SpiderProjects/淘寶搜索/__init__.py", line 30, ins.test() File "/Users/small_bud/Desktop/Python/SpiderProjects/淘寶搜索/__init__.py", line 27, in test print("test: ", self.name2) AttributeError: 'Sub' object has no attribute 'name2'
沒(méi)有name2屬性,而且Device2的初始化函數(shù)__init__沒(méi)有調(diào)用,所以name2屬性沒(méi)有被定義,那如果把Base換成super,結(jié)果怎么樣呢?
# -*- coding: utf-8 -*-class Base: def __init__(self): print("Base") self.name = "Base"class Device1(Base): def __init__(self): super().__init__() print("Device1") self.name1 = "Device1"class Device2(Base): def __init__(self): super().__init__() print("Device2") self.name2 = "Device2"class Sub(Device1, Device2): def __init__(self): super().__init__() print("Sub") self.name3 = "Sub" def test(self): print("test: ", self.name2) s = Sub() s.test()# 輸出:Base #Base.__init__中的printDevice2 #Device2.__init__中的printDevice1 #Device2.__init__中的printSub #Sub.__init__中的printtest: Device2 #test方法中的print
我們類的每一個(gè)函數(shù)首地址會(huì)被存儲(chǔ)起來(lái),當(dāng)我們用類名去調(diào)用一個(gè)函數(shù)的時(shí)候,我們可以理解為它代表一個(gè)絕對(duì)的地址,可以絕對(duì)定位到函數(shù)的地址。這個(gè)時(shí)候你可以把它理解為一個(gè)普通函數(shù)def Base.__init__(self),函數(shù)名是Base.__init__,參數(shù)是self
# 這是一個(gè)測(cè)試類,只為創(chuàng)建一個(gè)空對(duì)象class Test: passclass Base: def __init__(self): self.name = "aaaa"def func(obj): obj.age = 111t = Test() Base.__init__(t) print(t.name) func(t) print(t.age)#輸出:aaaa111
看到了,Base.__init__和func是一樣的,這絕不是我們所希望的類函數(shù)。那么為什么super會(huì)正確的找到要執(zhí)行的函數(shù)呢?看一下下面的例子:
class A: def __init__(self): print("A")class B: def __init__(self): super().__init__() print("B")class C(B, A): def __init__(self): super().__init__() print("C")class D(C): def __init__(self): super().__init__() print("D") D() print(D.mro())#輸出:A B C D [, , , , ]
我們知道子類的對(duì)象初始化時(shí),將先執(zhí)行父類的初始化,然后才執(zhí)行子類初始化,從初始化打印信息也可以看出來(lái),A>B>C>D,再看一下mro()函數(shù)的打印信息,這里展示了當(dāng)前類及其父類的類名,我們可以這樣理解每一個(gè)類被定義后,其存儲(chǔ)在程序存儲(chǔ)區(qū),除了類方法,還存在一個(gè)繼承管理表,表中按照一定的順序存儲(chǔ)著類及其父類的類名,類的初始化就按照表中的順序進(jìn)行初始化,按照以上的順序object>A>B>C>D,從object開(kāi)始初始化,然后是A、B、C、D。之前Base.__init__的調(diào)用我們分析過(guò)了,現(xiàn)在super這種方式呢?它是根據(jù)mro列表中記錄的類,按照順序依次調(diào)用,這樣就不會(huì)出現(xiàn)一個(gè)類被重復(fù)調(diào)用的情況。MRO列表是通過(guò)一種算法計(jì)算出順序的,我們不用關(guān)注它,不過(guò)要記住以下幾個(gè)準(zhǔn)則:
子類會(huì)先于父類被檢查
子類一定是在列表的第一位
多個(gè)父類會(huì)根據(jù)它們?cè)诹斜碇械捻樞虮粰z查
C繼承自A、B,A和B哪個(gè)先被調(diào)用,根據(jù)他們?cè)诒碇械捻樞?/p>
如果對(duì)下一個(gè)類存在兩個(gè)合法的選擇,選擇第一個(gè)父類
通過(guò)上面的分析,我們知道在類繼承中,一定要使用super的方式才能正確調(diào)用類繼承關(guān)系,在python3中推薦使用super().__init__,pytho2中使用super(Base, self).__init__。
以上是Python中super().__init__和Base.__init__的區(qū)別是什么的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!