您好,登錄后才能下訂單哦!
小編給大家分享一下Python中super().__init__和Base.__init__的區別是什么,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
我們在使用python中的類繼承時,子類繼承父類后,在重載父類的方法后,在方法中如果要執行父類對應的方法,一般有兩種方式: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類中,有一個move方法用來移動位置,在子類中我們想要增加一個記錄,記住每次移動的偏移量,那么我們就在子類中重載move函數,執行我們自定義的操作(記錄偏移量),然后繼續執行父類的move動作,直接調用父類move函數就可以避免我們重新實現移動位置的動作。
在上面的例子中,我們使用了super來調用父類方法,那么能不能使用Base來調用呢?
.... Base.__init__(self) ...... Base.move(self, x, y) ....
可以看到Base調用父類函數時,必須在函數中傳遞self參數。之前的文章中我們了解到類的普通函數調用需要使用類對象調用,而類的普通函數第一個參數默認是self,調用時不需要傳遞此參數,因為通過對象調用會自動傳遞。但是直接使用Base類名調用時,方法內部需要知道self是誰。那么兩種方式都可以,他們有區別嗎?當然是有的
首先看一下super的定義
class super(object): """ super() -> same as super(__class__, <first argument>) 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有四種調用方式
super(): 相當于super(當前類名, 第一個參數) python3中新增的方式
super(type):沒有綁定對象
super(type, obj):綁定對象,要求obj的類型是type或者其子類
super(type, type2):綁定對象,要求type2是type的子類
這里我們就先說一下super()和super(type, obj),這是我們常用的方式
在上面的例子中我們看到super和Base的方式一樣,接下來我們再看一個例子
# -*- 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
四個類,Base初始化函數被調用了兩次,為什么呢?Sub.__init__中Device1和Device2都調用了初始化方法,是這個原因嗎?準確點來講,是的,可不可以只調用一個,那么Base就只會被調用一次嘍,如果只調用Device1.__init__會有什么結果?
Base Device1 Sub Traceback (most recent call last): File "/Users/small_bud/Desktop/Python/SpiderProjects/淘寶搜索/__init__.py", line 30, in <module> s.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'
沒有name2屬性,而且Device2的初始化函數__init__沒有調用,所以name2屬性沒有被定義,那如果把Base換成super,結果怎么樣呢?
# -*- 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
我們類的每一個函數首地址會被存儲起來,當我們用類名去調用一個函數的時候,我們可以理解為它代表一個絕對的地址,可以絕對定位到函數的地址。這個時候你可以把它理解為一個普通函數def Base.__init__(self),函數名是Base.__init__,參數是self
# 這是一個測試類,只為創建一個空對象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是一樣的,這絕不是我們所希望的類函數。那么為什么super會正確的找到要執行的函數呢?看一下下面的例子:
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 [<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
我們知道子類的對象初始化時,將先執行父類的初始化,然后才執行子類初始化,從初始化打印信息也可以看出來,A>B>C>D,再看一下mro()函數的打印信息,這里展示了當前類及其父類的類名,我們可以這樣理解每一個類被定義后,其存儲在程序存儲區,除了類方法,還存在一個繼承管理表,表中按照一定的順序存儲著類及其父類的類名,類的初始化就按照表中的順序進行初始化,按照以上的順序object>A>B>C>D,從object開始初始化,然后是A、B、C、D。之前Base.__init__的調用我們分析過了,現在super這種方式呢?它是根據mro列表中記錄的類,按照順序依次調用,這樣就不會出現一個類被重復調用的情況。MRO列表是通過一種算法計算出順序的,我們不用關注它,不過要記住以下幾個準則:
子類會先于父類被檢查
子類一定是在列表的第一位
多個父類會根據它們在列表中的順序被檢查
C繼承自A、B,A和B哪個先被調用,根據他們在表中的順序
如果對下一個類存在兩個合法的選擇,選擇第一個父類
通過上面的分析,我們知道在類繼承中,一定要使用super的方式才能正確調用類繼承關系,在python3中推薦使用super().__init__,pytho2中使用super(Base, self).__init__。
以上是Python中super().__init__和Base.__init__的區別是什么的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。