·類(2)
@ 繼承(inheritance)
什麼是繼承:編程
B繼承A:A是父類(超類),B是子類(基類)。繼承能夠實現代碼重複利用,實現屬性和方法繼承。函數
繼承可使子類擁有父類的屬性和方法,也能夠從新定義某些屬性、重寫某些方法,即覆蓋父類原有的屬性和方法,使其得到父類不一樣的功能。固然,也能夠在子類中新設置屬性和方法。從技術上看,OOP裏繼承最主要的用途是實現多態,對於多態而言,最重要的是接口的繼承性(屬性和方法是否存在繼承性),這是不必定的。繼承也不是全爲了代碼的重複利用,而是爲了理順關係。post
對於 Python 中的繼承,前面一直在使用,那就是咱們寫的類都是新式類,全部新式類都是繼承自 object 類。不要忘記,新式類的一種寫法:spa
class NewStyle(object): pass
這就是典型的繼承。code
繼承的基本概念:orm
class Person: def __init__(self,name): self.name = name def get_name(self): print ("hello,{}!".format(self.name)) def setHeight(self, n): self.length = n def breast(self, n): print ("My breast is: ",n) class Boy(Person): def setHeight(self): print ("The height is:1.80m .") j = Boy('jimmy') j.get_name() j.setHeight() j.breast(90) 打印結果: hello,jimmy! The height is:1.80m . My breast is: 90
首先,定義了一個父類 Person,定義一個子類 Boy,Boy類的括號中是Person,這就意味着 Boy 繼承了 Person,Boy 是 Person 的子類,Person 是 Boy 的父類。那麼 Boy 就所有擁有了 Person 中的方法和屬性。blog
若是 Boy 裏面有一個和 Person 一樣名稱的方法,那麼就把 Person 中的同一個方法遮蓋住了,顯示的是 Boy 中的方法,這叫作方法的重寫。實例化類 Boy以後,執行實例方法 j.setHeight(),因爲在類 Boy中重寫了 setHeight 方法,那麼 Person 中的那個方法就不顯做用了,在這個實例方法中執行的是類 Boy 中的方法。繼承
雖然在類 Boy 中沒有看到 get_name() 方法,可是由於它繼承了 Person,因此 j.get_name() 就執行類 Person 中的方法。同理 j.breast(90) ,它們就好像是在類 Boy 裏面已經寫了這兩個方法同樣。既然繼承了,就可使用。接口
多重繼承:get
子類繼承多個父類:
class Person: def eye(self): print("two eyss") def breast(self,n): print("the breast is:",n) class Girl: age = 18 def color(self): print("the girl is white.") class HotGirl(Person,Girl): pass
在類的名字後面的括號中把所繼承的兩個類的名字寫上,HotGirl 就繼承了Person 和 Girl這兩個類。實例化類 HotGirl,既然繼承了上面的兩個類,那麼那兩個類的方法就都可以拿過來使用。在類 Girl 中, age = 28,在對 HotGirl 實例化以後,由於繼承的緣由,這個類屬性也被繼承到 HotGirl 中,所以經過實例獲得它。
繼承的特色,即將父類的方法和屬性所有承接到子類中;若是子類重寫了父類的方法,就使用子類的該方法,父類的被遮蓋。
多重繼承的順序:
若是一個子類繼承了兩個父類,而且兩個父類有一樣的方法或者屬性,那麼在實例化子類後,調用那個方法或屬性,是屬於哪一個父類的呢?造一個沒有實際意義,純粹爲了解決這個問題的程序:
class K1(object): def foo(self): print("K1-foo") class K2(object): def foo(self): print("K2-foo") def bar(self): print("K2-bar") class J1(K1, K2): pass class J2(K1, K2): def bar(self): print("J2-bar") class C(J1, J2): pass print(C.__mro__) m = C() m.foo() m.bar() 打印結果: (<class '__main__.C'>, <class '__main__.J1'>, <class '__main__.J2'>, <class '__main__.K1'>, <class '__main__.K2'>, <class 'object'>) K1-foo J2-bar
print C.__mro__打印出類的繼承順序。
從上面清晰看出來了,若是要執行 foo() 方法,首先看 J1,沒有,看 J2,尚未,看 J1 裏面的 K1,有了,即 C(沒有)==>J1(沒有)==>J2(沒有)==>K1(找到了);bar() 也是按照這個順序,在 J2 中就找到了一個。
這種對繼承屬性和方法搜索的順序稱之爲「廣度優先」。新式類用以及 Python3.x 中都是按照此順序原則搜尋屬性和方法的。
可是,在舊式類中,是按照「深度優先」的順序的。由於後面讀者也基本不用舊式類,因此不舉例。
@ super函數
對於初始化函數的繼承,跟通常方法的繼承不一樣:
class Person: def __init__(self): self.height = 160 def about(self, name): print("{} is about {}".format(name, self.height)) class Girl(Person): def __init__(self): self.breast = 90 def about(self, name): print("{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast)) cang = Girl() cang.about("wangguniang")
打印結果:
Traceback (most recent call last):
File "test1.py", line 14, in <module>
cang.about("wangguniang")
File "test1.py", line 11, in about
print("{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast))
AttributeError: 'Girl' object has no attribute 'height'
在上面這段程序中,類 Girl 繼承了類 Person。在類 Girl 中,初始化設置了 self.breast = 90,因爲繼承了 Person,按照前面的經驗,Person 的初始化函數中的 self.height = 160 也應該被 Girl 所繼承過來。而後在重寫的 about 方法中,就是用 self.height。
實例化類 Girl,並執行 cang.about("wangguniang"),試圖打印出一句話 wangguniang is a hot girl, she is about 160, and her bereast is 90。保存程序,運行報錯!
信息顯示 self.height 是不存在的。也就是說類 Girl 沒有從 Person 中繼承過來這個屬性。
在 Girl中發現, about 方法重寫了,__init__方法,也被重寫了。它跟類 Person 中的__init__重名了,也一樣是重寫了那個初始化函數。這是由於在子類中重寫了某個方法以後,父類中一樣的方法被遮蓋了。
使用super 函數:
class Person: def __init__(self): self.height = 160 def about(self, name): print("{} is about {}".format(name, self.height)) class Girl(Person): def __init__(self): super().__init__() self.breast = 90 def about(self, name): print("{} is a hot girl, she is about {}, and her breast is {}".format(name, self.height, self.breast)) cang = Girl() cang.about("wangguniang") 打印結果: wangguniang is a hot girl, she is about 160, and her breast is 90
在子類中,__init__方法重寫了,爲了調用父類同方法,使用 super(Girl, self).__init__()的方式。super 函數的參數,第一個是當前子類的類名字,第二個是 self,而後是點號,點號後面是所要調用的父類的方法。一樣在子類重寫的 about 方法中,也能夠調用父類的 about 方法。
最後要提醒注意:super 函數僅僅適用於新式類。
@ 綁定方法與非綁定方法
要經過實例來調用類的方法(函數),常常要將類實例化。方法是類內部定義函數,只不過這個函數的第一個參數是 self。(能夠認爲方法是類屬性,但不是實例屬性)。必須將類實例化以後,才能經過實例調用該類的方法。調用的時候在方法後面要跟括號(括號中默認有 self 參數,能夠不寫出來)。經過實例調用方法,稱這個方法綁定在實例上。