[TOC]
## 初識繼承
**什么是繼承?**
繼承指的是類與類之間的關系,是一種什么“是”什么的關系,繼承的功能之一就是用來解決代碼重用問題
繼承是一種創建新類的方式,在python中,新建的類可以繼承一個或多個父類,父類又稱為基類或超類,新建的類稱為派生類或子類
**python中類的繼承分為:單繼承和多繼承**
~~~
class ParentClass1: #定義父類
pass
class ParentClass2: #定義父類
pass
class SubClass1(ParentClass1): #單繼承
pass
class SubClass2(ParentClass1,ParentClass2): #多繼承用逗號分隔
pass
~~~
**查看繼承**
`__base__`只查看從左到右繼承的第一個子類,`__bases__`則是查看所有繼承的父類
~~~
>>> SubClass1.__bases__
(<class '__main__.ParentClass1'>,)
>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
~~~
### 繼承與抽象
**抽象即抽取類似或者說比較像的部分**。抽象分成兩個層次:
1.將奧巴馬和梅西這倆對象比較像的部分抽取成類(人類);
2.將人,豬,狗這三個類比較像的部分抽取成父類(動物類)。
抽象最主要的作用是劃分類別,可以隔離關注點,降低復雜度.
**繼承:是基于抽象的結果,通過編程語言去實現它**
肯定是先經歷抽象這個過程,才能通過繼承的方式去表達出抽象的結構。
### 繼承與重用性
在開發程序的過程中,定義了一個類A,又想新建立另外一個類B,但是類B的大部分內容與類A的相同時
通過繼承的方式新建類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 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
~~~
提示:用已經有的類建立一個新的類,這樣就重用了已經有的軟件中的一部分設置,大大節省了編程工作量,這就是常說的軟件重用,不僅可以重用自己的類,也可以繼承別人的,比如標準庫,來定制新的數據類型,這樣就是大大縮短了軟件開發周期,對大型軟件開發來說,意義重大.
### 繼承的實現原理
對于定義的每一個類,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列表遵循如下準則:
1. 子類會先于父類被檢查
2. 多個父類會根據它們在列表中的順序被檢查
3. 如果對下一個類存在兩個合法的選擇,選擇第一個父類
4. 經典類查找順序遵循深度優先規則
5. 新式類查找順序遵循廣度優先規則
**示范代碼**
~~~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__) #只有新式才有這個屬性可以查看線性列表,經典類沒有這個屬性
#結果
from D
(<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
~~~
> 新式類繼承順序:F->D->B->E->C->A
>經典類繼承順序:F->D->B->A->E->C
## 派生與覆蓋
當然子類也可以添加自己新的屬性或者在自己這里重新定義這些屬性(不會影響到父類),需要注意的是,一旦重新定義了自己的屬性且與父類重名,那么調用新增的屬性時,就以自己為準了,相當于覆蓋了父類的屬性。
~~~
class Riven(Hero):
camp='Noxus'
def attack(self,enemy): #在自己這里定義新的attack,不再使用父類的attack,且不會影響父類
print('from riven')
def fly(self): #在自己這里定義新的
print('%s is flying' %self.nickname)
~~~
### 在子類中調用父類的方法
在子類派生出的新方法中,往往需要重用父類的方法,有兩種方式實現
方式一:指名道姓,即父類名.父類方法()
~~~
class Vehicle: #定義交通工具類
Country='China'
def __init__(self,name,speed):
self.name=name
self.speed=speed
def run(self):
print('開動啦...')
class Subway(Vehicle): #地鐵
def __init__(self,name,speed,line):
Vehicle.__init__(self,name,speed)
self.line=line
def run(self):
print('地鐵%s號線歡迎您' %self.line)
Vehicle.run(self)
line13=Subway('中國地鐵','180m/s',13)
line13.run()
~~~
方式二:super()方法
~~~
class Vehicle: #定義交通工具類
Country='China'
def __init__(self,name,speed):
self.name=name
self.speed=speed
def run(self):
print('開動啦...')
class Subway(Vehicle): #地鐵
def __init__(self,name,speed):
#super(Subway,self) 就相當于實例本身 在python3中super()等同于super(Subway,self)
super().__init__(name,speed)
self.line=line
def run(self):
print('地鐵%s號線歡迎您' %self.line)
super(Subway,self).run()
line13=Subway('中國地鐵','180m/s',13)
line13.run()
~~~
兩種方式的區別:
方式一是跟繼承沒有關系的,
方式二的super()是依賴于繼承的,且即使沒有直接繼承關系,super仍然會按照mro繼續往后查找
~~~
#A沒有繼承B,但是A內super會基于C中的C.mro()繼續往后找
class A:
def test(self):
print('from A')
super().test()
class B:
def test(self):
print('from B')
class C(A,B):
pass
info=C()
info.test()
print(C.mro())
#打印結果:
from A
from B
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
~~~
## 組合與重用性
軟件重用的重要方式除了繼承之外還有另外一種方式,即:組合
組合指的是,在一個類中以另外一個類的對象作為數據屬性,稱為類的組合
~~~
class Equip: #武器裝備類
def fire(self):
print('release Fire skill')
class Riven: #英雄Riven的類,
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課程,教授有學生s1、s2、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()
~~~
- 基礎部分
- 基礎知識
- 變量
- 數據類型
- 數字與布爾詳解
- 列表詳解list
- 字符串詳解str
- 元組詳解tup
- 字典詳解dict
- 集合詳解set
- 運算符
- 流程控制與循環
- 字符編碼
- 編的小程序
- 三級菜單
- 斐波那契數列
- 漢諾塔
- 文件操作
- 函數相關
- 函數基礎知識
- 函數進階知識
- lambda與map-filter-reduce
- 裝飾器知識
- 生成器和迭代器
- 琢磨的小技巧
- 通過operator函數將字符串轉換回運算符
- 目錄規范
- 異常處理
- 常用模塊
- 模塊和包相關概念
- 絕對導入&相對導入
- pip使用第三方源
- time&datetime模塊
- random隨機數模塊
- os 系統交互模塊
- sys系統模塊
- shutil復制&打包模塊
- json&pickle&shelve模塊
- xml序列化模塊
- configparser配置模塊
- hashlib哈希模塊
- subprocess命令模塊
- 日志logging模塊基礎
- 日志logging模塊進階
- 日志重復輸出問題
- re正則表達式模塊
- struct字節處理模塊
- abc抽象類與多態模塊
- requests與urllib網絡訪問模塊
- 參數控制模塊1-optparse-過時
- 參數控制模塊2-argparse
- pymysql數據庫模塊
- requests網絡請求模塊
- 面向對象
- 面向對象相關概念
- 類與對象基礎操作
- 繼承-派生和組合
- 抽象類與接口
- 多態與鴨子類型
- 封裝-隱藏與擴展性
- 綁定方法與非綁定方法
- 反射-字符串映射屬性
- 類相關內置方法
- 元類自定義及單例模式
- 面向對象的軟件開發
- 網絡-并發編程
- 網絡編程SOCKET
- socket簡介和入門
- socket代碼實例
- 粘包及粘包解決辦法
- 基于UDP協議的socket
- 文件傳輸程序實戰
- socketserver并發模塊
- 多進程multiprocessing模塊
- 進程理論知識
- 多進程與守護進程
- 鎖-信號量-事件
- 隊列與生產消費模型
- 進程池Pool
- 多線程threading模塊
- 進程理論和GIL鎖
- 死鎖與遞歸鎖
- 多線程與守護線程
- 定時器-條件-隊列
- 線程池與進程池(新方法)
- 協程與IO模型
- 協程理論知識
- gevent與greenlet模塊
- 5種網絡IO模型
- 非阻塞與多路復用IO實現
- 帶著目標學python
- Pycharm基本使用
- 爬蟲
- 案例-爬mzitu美女
- 案例-爬小說
- beautifulsoup解析模塊
- etree中的xpath解析模塊
- 反爬對抗-普通驗證碼
- 反爬對抗-session登錄
- 反爬對抗-代理池
- 爬蟲技巧-線程池
- 爬蟲對抗-圖片懶加載
- selenium瀏覽器模擬