一:初識繼承
1,什麼是繼承?
繼承指的是類與類之間的關係,是一種什麼「是」什麼的關係,繼承的功能之一就是用來解決代碼重用問題html
繼承是一種建立新類的方式,在python中,新建的類能夠繼承一個或多個父類,父類又能夠成爲基類或超類,新建的類稱爲派生類或子類java
2,python中類的繼承分爲:單繼承和多繼承
class ParentClass1: #定義父類 pass class ParentClass2: #定義父類 pass class SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClass pass class SubClass2(ParentClass1,ParentClass2): #python支持多繼承,用逗號分隔開多個繼承的類 pass
查看繼承python
方法:linux
__bases__則是查看全部繼承的父類
實例:git
>>> SubClass1.__bases__ #__base__只查看從左到右繼承的第一個子類,__bases__則是查看全部繼承的父類 (<class '__main__.ParentClass1'>,) >>> SubClass2.__bases__ (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
經典類與新式類github
1.只有在python2中才分新式類和經典類,python3中統一都是新式類 2.在python2中,沒有顯式的繼承object類的類,以及該類的子類,都是經典類 3.在python2中,顯式地聲明繼承object的類,以及該類的子類,都是新式類 4.在python3中,不管是否繼承object,都默認繼承object,即python3中全部類均爲新式類
提示:若是沒有指定基類,python的類會默認繼承object類,object是全部python類的基類,它提供了一些常見方法(如__str__)的實現。算法
>>> ParentClass1.__bases__ (<class 'object'>,) >>> ParentClass2.__bases__ (<class 'object'>,)
二:繼承與抽象(先抽象再繼承)
繼承描述的是子類與父類之間的關係,是一種什麼是什麼的關係,要找出這種關係,必須先抽象再繼承。抽象即抽取相似或者說比較像的部分。編程
抽象分紅兩個層次:設計模式
1.將奧巴馬和梅西這倆對象比較像的部分抽取成類;網絡
2.將人,豬,狗這三個類比較像的部分抽取成父類。
抽象最主要的做用是劃分類別(能夠隔離關注點,下降複雜度)
繼承:是基於抽象的結果,經過編程語言去實現它,確定是先經歷抽象這個過程,才能經過繼承的方式去表達出抽象的結構。
抽象只是分析和設計的過程當中,一個動做或者說一種技巧,經過抽象能夠獲得類
三:繼承與重用性
首先舉一個例子,使用繼承來重用代碼比較好的例子
==========================第一部分 例如 貓能夠:喵喵叫、吃、喝、拉、撒 狗能夠:汪汪叫、吃、喝、拉、撒 若是咱們要分別爲貓和狗建立一個類,那麼就須要爲 貓 和 狗 實現他們全部的功能,僞代碼以下: #貓和狗有大量相同的內容 class 貓: def 喵喵叫(self): print '喵喵叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something class 狗: def 汪汪叫(self): print '喵喵叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something ==========================第二部分 上述代碼不難看出,吃、喝、拉、撒是貓和狗都具備的功能,而咱們卻分別的貓和狗的類中編寫了兩次。
若是使用 繼承 的思想,以下實現: 動物:吃、喝、拉、撒 貓:喵喵叫(貓繼承動物的功能) 狗:汪汪叫(狗繼承動物的功能) 僞代碼以下: class 動物: def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something # 在類後面括號中寫入另一個類名,表示當前類繼承另一個類 class 貓(動物): def 喵喵叫(self): print '喵喵叫' # 在類後面括號中寫入另一個類名,表示當前類繼承另一個類 class 狗(動物): def 汪汪叫(self): print '喵喵叫' ==========================第三部分 #繼承的代碼實現 class Animal: def eat(self): print("%s 吃 " %self.name) def drink(self): print ("%s 喝 " %self.name) def shit(self): print ("%s 拉 " %self.name) def pee(self): print ("%s 撒 " %self.name) class Cat(Animal): def __init__(self, name): self.name = name self.breed = '貓' def cry(self): print('喵喵叫') class Dog(Animal): def __init__(self, name): self.name = name self.breed='狗' def cry(self): print('汪汪叫') # ######### 執行 ######### c1 = Cat('小白家的小黑貓') c1.eat() c2 = Cat('小黑的小白貓') c2.drink() d1 = Dog('胖子家的小瘦狗') d1.eat() 使用繼承來重用代碼比較好的例子
在開發程序的過程當中,若是咱們定義了一個類A,而後又想新創建另一個類B,可是類B的大部份內容與類A的相同時
咱們不可能從頭開始寫一個類B,這就用到了類的繼承的概念。
經過繼承的方式新建類B,讓B繼承A,B會‘遺傳’A的全部屬性(數據屬性和函數屬性),實現代碼重用
class Hero: def __init__(self,nickname,aggressivity,life_value): self.nickname=nickname self.aggressivity=aggressivity self.life_value=life_value def move_forward(self): print('%s move forward' %self.nickname) def move_backward(self): print('%s move backward' %self.nickname) def move_left(self): print('%s move forward' %self.nickname) def move_right(self): print('%s move forward' %self.nickname) def attack(self,enemy): enemy.life_value-=self.aggressivity class Garen(Hero): pass class Riven(Hero): pass g1=Garen('草叢倫',100,300) r1=Riven('銳雯雯',57,200) print(g1.life_value) #結果:300 r1.attack(g1) print(g1.life_value) #結果:243
提示:用已經有的類創建一個新的類,這樣就重用了已經有的軟件中的一部分設置大部分,大大節省了編程工做量,這就是常說的軟件重用,不只能夠重用本身的類,也能夠繼承別人的,好比標準庫,來定製新的數據類型,這樣就是大大縮短了軟件開發週期,對大型軟件開發來講,意義重大.
重點!!!再看屬性查找
提示:像g1.life_value之類的屬性引用,會先從實例中找life_value而後去類中找,而後再去父類中找...直到最頂級的父類。那麼如何解釋下面的打印結果呢?
class Foo: def f1(self): print('from Foo.f1') def f2(self): print('from Foo.f2') class Bar(Foo): def f1(self): print('from Bar.f1') b = Bar() print(b.__dict__) #{} b.f1() #from Bar.f1 #找的話確定先從本身上面找,沒有的話再找父類 class Foo: def f1(self): print('from Foo.f1') def f2(self): print('from Foo.f2') self.f1() #b.f1() class Bar(Foo): def f1(self): print('from Bar.f1') b = Bar() b.f2() # from Foo.f2 # from Bar.f1
四:派生
固然子類也能夠添加本身新的屬性或者在本身這裏從新定義這些屬性(不會影響到父類),須要注意的是,一旦從新定義了本身的屬性且與父類重名,那麼調用新增的屬性時,就以本身爲準了。
class Riven(Hero): camp='Noxus' def attack(self,enemy): #在本身這裏定義新的attack,再也不使用父類的attack,且不會影響父類 print('from riven') def fly(self): #在本身這裏定義新的 print('%s is flying' %self.nickname)
在子類中,新建的重名的函數屬性,在編輯函數內功能的時候,有可能須要重用父類中重名的那個函數功能,應該是用調用普通函數的方式,即:類名.func(),此時就與調用普通函數無異了,所以即使是self參數也要爲其傳值
class Riven(Hero): camp='Noxus' def __init__(self,nickname,aggressivity,life_value,skin): Hero.__init__(self,nickname,aggressivity,life_value) #調用父類功能 self.skin=skin #新屬性 def attack(self,enemy): #在本身這裏定義新的attack,再也不使用父類的attack,且不會影響父類 Hero.attack(self,enemy) #調用功能 print('from riven') def fly(self): #在本身這裏定義新的 print('%s is flying' %self.nickname) r1=Riven('銳雯雯',57,200,'比基尼') r1.fly() print(r1.skin) ''' 運行結果 銳雯雯 is flying 比基尼 '''
五,繼承的實現原理
1.繼承原理(python如何實現的繼承)
python究竟是如何實現繼承的,對於你定義的每個類,python會計算出一個方法解析順序(MRO)列表,這個MRO列表就是一個簡單的全部基類的線性順序列表,例如
>>> F.mro() #等同於F.__mro__ [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
爲了實現繼承,python會在MRO列表上從左到右開始查找基類,直到找到第一個匹配這個屬性的類爲止。而這個MRO列表的構造是經過一個C3線性化算法來實現的。咱們不去深究這個算法的數學原理,它實際上就是合併全部父類的MRO列表並遵循以下三條準則:
- 子類會先於父類被檢查
- 多個父類會根據它們在列表中的順序被檢查
- 若是對下一個類存在兩個合法的選擇,選擇第一個父類
2.繼承順序
在Java和C#中子類只能繼承一個父類,而Python中子類能夠同時繼承多個父類,若是繼承了多個父類,那麼屬性的查找方式有兩種,分別是:深度優先和廣度優先
示範代碼
class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') class E(C): def test(self): print('from E') class F(D,E): # def test(self): # print('from F') pass f1=F() f1.test() print(F.__mro__) #只有新式纔有這個屬性能夠查看線性列表,經典類沒有這個屬性 #新式類繼承順序:F->D->B->E->C->A #經典類繼承順序:F->D->B->A->E->C #python3中統一都是新式類 #pyhon2中才分新式類與經典類
六:組合與重用性
軟件重用的重要方式除了繼承以外還有另一種方式,即:組合
組合指的是,在一個類中以另一個類的對象做爲數據屬性,稱爲類的組合
>>> class Equip: #武器裝備類 ... def fire(self): ... print('release Fire skill') ... >>> class Riven: #英雄Riven的類,一個英雄須要有裝備,於是須要組合Equip類 ... camp='Noxus' ... def __init__(self,nickname): ... self.nickname=nickname ... self.equip=Equip() #用Equip類產生一個裝備,賦值給實例的equip屬性 ... >>> r1=Riven('銳雯雯') >>> r1.equip.fire() #可使用組合的類產生的對象所持有的方法 release Fire skill
組合與繼承都是有效地利用已有類的資源的重要方式。可是兩者的概念和使用場景皆不一樣,
1.繼承的方式
經過繼承創建了派生類與基類之間的關係,它是一種'是'的關係,好比白馬是馬,人是動物。
當類之間有不少相同的功能,提取這些共同的功能作成基類,用繼承比較好,好比老師是人,學生是人
2.組合的方式
用組合的方式創建了類與組合的類之間的關係,它是一種‘有’的關係,好比教授有生日,教授教python和linux課程,教授有學生s一、s二、s3...
示例:繼承與組合
class People: def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex class Course: def __init__(self,name,period,price): self.name=name self.period=period self.price=price def tell_info(self): print('<%s %s %s>' %(self.name,self.period,self.price)) class Teacher(People): def __init__(self,name,age,sex,job_title): People.__init__(self,name,age,sex) self.job_title=job_title self.course=[] self.students=[] class Student(People): def __init__(self,name,age,sex): People.__init__(self,name,age,sex) self.course=[] egon=Teacher('egon',18,'male','沙河霸道金牌講師') s1=Student('牛榴彈',18,'female') python=Course('python','3mons',3000.0) linux=Course('python','3mons',3000.0) #爲老師egon和學生s1添加課程 egon.course.append(python) egon.course.append(linux) s1.course.append(python) #爲老師egon添加學生s1 egon.students.append(s1) #使用 for obj in egon.course: obj.tell_info()
總結:
當類之間有顯著不一樣,而且較小的類是較大的類所須要的組件時,用組合比較好
七,在子類中調用父類的方法
在子類派生出的新方法中,每每須要重用父類的方法,咱們有兩種方式實現
方式一:指名道姓,即父類名.父類方法()
class Vehicle: #定義交通工具類 Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('開動啦...') class Subway(Vehicle): #地鐵 def __init__(self,name,speed,load,power,line): Vehicle.__init__(self,name,speed,load,power) self.line=line def run(self): print('地鐵%s號線歡迎您' %self.line) Vehicle.run(self) line13=Subway('中國地鐵','180m/s','1000人/箱','電',13) line13.run()
方法二:super()
class Vehicle: #定義交通工具類 Country='China' def __init__(self,name,speed,load,power): self.name=name self.speed=speed self.load=load self.power=power def run(self): print('開動啦...') class Subway(Vehicle): #地鐵 def __init__(self,name,speed,load,power,line): #super(Subway,self) 就至關於實例自己 在python3中super()等同於super(Subway,self) super().__init__(name,speed,load,power) self.line=line def run(self): print('地鐵%s號線歡迎您' %self.line) super(Subway,self).run() class Mobike(Vehicle):#摩拜單車 pass line13=Subway('中國地鐵','180m/s','1000人/箱','電',13) line13.run()
這兩種方式的區別是:方式一是跟繼承沒有關係的,而方式二的super()是依賴於繼承的,而且即便沒有直接繼承關係,super仍然會按照mro繼續日後查找(兩種方法用哪種均可以,可是最好不要混合使用)
#A沒有繼承B,可是A內super會基於C.mro()繼續日後找 class A: def test(self): super().test() class B: def test(self): print('from B') class C(A,B): pass c=C() c.test() #打印結果:from B print(C.mro()) #[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
瞭解內容:指名道姓與super()的區別
#指名道姓 class A: def __init__(self): print('A的構造方法') class B(A): def __init__(self): print('B的構造方法') A.__init__(self) class C(A): def __init__(self): print('C的構造方法') A.__init__(self) class D(B,C): def __init__(self): print('D的構造方法') B.__init__(self) C.__init__(self) pass f1=D() #A.__init__被重複調用 ''' D的構造方法 B的構造方法 A的構造方法 C的構造方法 A的構造方法 ''' #使用super() class A: def __init__(self): print('A的構造方法') class B(A): def __init__(self): print('B的構造方法') super(B,self).__init__() class C(A): def __init__(self): print('C的構造方法') super(C,self).__init__() class D(B,C): def __init__(self): print('D的構造方法') super(D,self).__init__() f1=D() #super()會基於mro列表,日後找 ''' D的構造方法 B的構造方法 C的構造方法 A的構造方法 '''
當你使用super()函數時,Python會在MRO列表上繼續搜索下一個類。只要每一個重定義的方法統一使用super()並只調用它一次,那麼控制流最終會遍歷完整個MRO列表,每一個方法也只會被調用一次(注意注意注意:使用super調用的全部屬性,都是從MRO列表當前的位置日後找,千萬不要經過看代碼去找繼承關係,必定要看MRO列表)
八:接口與歸一化設計
1.什麼是接口
hi boy,給我開個查詢接口。。。此時的接口指的是:本身提供給使用者來調用本身功能的方式\方法\入口,java中的interface使用以下
=================第一部分:Java 語言中的接口很好的展示了接口的含義: IAnimal.java /* * Java的Interface接口的特徵: * 1)是一組功能的集合,而不是一個功能 * 2)接口的功能用於交互,全部的功能都是public,即別的對象可操做 * 3)接口只定義函數,但不涉及函數實現 * 4)這些功能是相關的,都是動物相關的功能,但光合做用就不適宜放到IAnimal裏面了 */ package com.oo.demo; public interface IAnimal { public void eat(); public void run(); public void sleep(); public void speak(); } =================第二部分:Pig.java:豬」的類設計,實現了IAnnimal接口 package com.oo.demo; public class Pig implements IAnimal{ //以下每一個函數都須要詳細實現 public void eat(){ System.out.println("Pig like to eat grass"); } public void run(){ System.out.println("Pig run: front legs, back legs"); } public void sleep(){ System.out.println("Pig sleep 16 hours every day"); } public void speak(){ System.out.println("Pig can not speak"); } } =================第三部分:Person2.java /* *實現了IAnimal的「人」,有幾點說明一下: * 1)一樣都實現了IAnimal的接口,但「人」和「豬」的實現不同,爲了不太多代碼致使影響閱讀,這裏的代碼簡化成一行,但輸出的內容不同,實際項目中同一接口的同一功能點,不一樣的類實現徹底不同 * 2)這裏一樣是「人」這個類,但和前面介紹類時給的類「Person」徹底不同,這是由於一樣的邏輯概念,在不一樣的應用場景下,具有的屬性和功能是徹底不同的 */ package com.oo.demo; public class Person2 implements IAnimal { public void eat(){ System.out.println("Person like to eat meat"); } public void run(){ System.out.println("Person run: left leg, right leg"); } public void sleep(){ System.out.println("Person sleep 8 hours every dat"); } public void speak(){ System.out.println("Hellow world, I am a person"); } } =================第四部分:Tester03.java package com.oo.demo; public class Tester03 { public static void main(String[] args) { System.out.println("===This is a person==="); IAnimal person = new Person2(); person.eat(); person.run(); person.sleep(); person.speak(); System.out.println("\n===This is a pig==="); IAnimal pig = new Pig(); pig.eat(); pig.run(); pig.sleep(); pig.speak(); } } java中的interface
PS:hi boy,給我開個查詢接口。。。此時的接口指的是:本身提供給使用者來調用本身功能的方式\方法\入口
2.爲何要用接口
接口提取了一羣類共同的函數,能夠把接口當作一個函數的集合。
而後讓子類去實現接口中的函數。
這麼作的意義在於歸一化,什麼叫歸一化,就是隻要是基於同一個接口實現的類,那麼全部的這些類產生的對象在使用時,從用法上來講都同樣。
歸一化的好處在於:
1. 歸一化讓使用者無需關心對象的類是什麼,只須要的知道這些對象都具有某些功能就能夠了,這極大地下降了使用者的使用難度。
2. 歸一化使得高層的外部使用者能夠不加區分的處理全部接口兼容的對象集合
2.1:就好象linux的泛文件概念同樣,全部東西均可以當文件處理,沒必要關心它是內存、磁盤、網絡仍是屏幕(固然,對底層設計者,固然也能夠區分出「字符設備」和「塊設備」,而後作出針對性的設計:細緻到什麼程度,視需求而定)。
2.2:再好比:咱們有一個汽車接口,裏面定義了汽車全部的功能,而後由本田汽車的類,奧迪汽車的類,大衆汽車的類,他們都實現了汽車接口,這樣就好辦了,你們只須要學會了怎麼開汽車,那麼不管是本田,仍是奧迪,仍是大衆咱們都會開了,開的時候根本無需關心我開的是哪一類車,操做手法(函數調用)都同樣
3.模仿interface
在python中根本就沒有一個叫作interface的關鍵字,若是非要去模仿接口的概念
能夠藉助第三方模塊:
http://pypi.python.org/pypi/zope.interface
twisted的twisted\internet\interface.py裏使用zope.interface
文檔https://zopeinterface.readthedocs.io/en/latest/
設計模式:https://github.com/faif/python-patterns
也可使用繼承:
繼承的兩種用途
一:繼承基類的方法,而且作出本身的改變或者擴展(代碼重用):實踐中,繼承的這種用途意義並不很大,甚至經常是有害的。由於它使得子類與基類出現強耦合。
二:聲明某個子類兼容於某基類,定義一個接口類(模仿java的Interface),接口類中定義了一些接口名(就是函數名)且並未實現接口的功能,子類繼承接口類,而且實現接口中的功能
class Interface:#定義接口Interface類來模仿接口的概念,python中壓根就沒有interface關鍵字來定義一個接口。 def read(self): #定接口函數read pass def write(self): #定義接口函數write pass class Txt(Interface): #文本,具體實現read和write def read(self): print('文本數據的讀取方法') def write(self): print('文本數據的讀取方法') class Sata(Interface): #磁盤,具體實現read和write def read(self): print('硬盤數據的讀取方法') def write(self): print('硬盤數據的讀取方法') class Process(Interface): def read(self): print('進程數據的讀取方法') def write(self): print('進程數據的讀取方法')
上面的代碼只是看起來像接口,其實並無起到接口的做用,子類徹底能夠不用去實現接口 ,這就用到了抽象類
九,抽象類
1.什麼是抽象類
與java同樣,python也有抽象類的概念可是一樣須要藉助模塊實現,抽象類是一個特殊的類,它的特殊之處在於只能被繼承,不能被實例化
2.爲何要有抽象類
若是說類是從一堆對象中抽取相同的內容而來的,那麼抽象類就是從一堆類中抽取相同的內容而來的,內容包括數據屬性和函數屬性。
好比咱們有香蕉的類,有蘋果的類,有桃子的類,從這些類抽取相同的內容就是水果這個抽象的類,你吃水果時,要麼是吃一個具體的香蕉,要麼是吃一個具體的桃子。。。。。。你永遠沒法吃到一個叫作水果的東西。
從設計角度去看,若是類是從現實對象抽象而來的,那麼抽象類就是基於類抽象而來的。
從實現角度來看,抽象類與普通類的不一樣之處在於:抽象類中只能有抽象方法(沒有實現功能),該類不能被實例化,只能被繼承,且子類必須實現抽象方法。這一點與接口有點相似,但實際上是不一樣的,即將揭曉答案
3.在python中實現抽象類
#_*_coding:utf-8_*_ #一切皆文件 import abc #利用abc模塊實現抽象類 class All_file(metaclass=abc.ABCMeta): all_type='file' @abc.abstractmethod #定義抽象方法,無需實現功能 def read(self): '子類必須定義讀功能' pass @abc.abstractmethod #定義抽象方法,無需實現功能 def write(self): '子類必須定義寫功能' pass # class Txt(All_file): # pass # # t1=Txt() #報錯,子類沒有定義抽象方法 class Txt(All_file): #子類繼承抽象類,可是必須定義read和write方法 def read(self): print('文本數據的讀取方法') def write(self): print('文本數據的讀取方法') class Sata(All_file): #子類繼承抽象類,可是必須定義read和write方法 def read(self): print('硬盤數據的讀取方法') def write(self): print('硬盤數據的讀取方法') class Process(All_file): #子類繼承抽象類,可是必須定義read和write方法 def read(self): print('進程數據的讀取方法') def write(self): print('進程數據的讀取方法') wenbenwenjian=Txt() yingpanwenjian=Sata() jinchengwenjian=Process() #這樣你們都是被歸一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read() print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type)
4.抽象類與接口
抽象類的本質仍是類,指的是一組類的類似性,包括數據屬性(如all_type)和函數屬性(如read ,write),而接口只強調函數屬性的類似性
抽象類是一個介於類和接口直接的一個概念,同時具有類和接口的部分特性,能夠用來實現歸一化設計
參考內容:https://www.cnblogs.com/linhaifeng/p/6295875.html;寫在此處的目的是鞏固老師所講知識,課後爲了方便複習