Vue+Element UI 樹形控制元件整合下拉功能選單(tree + dropdown +input)
class繼承
一:繼承介紹
什麼是繼承?
繼承就是建立了一個新的類,在Python中,新建立的類可以繼承一個或者多個父類(現實社會有點難)新建的類就是子類,圍著派生類,父類稱之為基類或者超類(累)
為什麼要有繼承?
繼承可以減少程式碼冗餘
如何使用繼承?
class P1: pass class P2: pass class sub1(P1): # 繼承了P1類 pass class sub2(P1,P2): # 繼承了P1,P2類 pass 通過類內的__bases__可以檢視類繼承的所有的父類 print(sub1.__bases__) # (<class '__main__.P1'>,) print(sub2.__bases__) # (<class '__main__.P1'>, <class '__main__.P2'>)
示列一(繼承)
class School: scholl = '東京校區' class Student(School): def __init__(self,name,age,gender,suid,course): self.name = name self.age = age self.gender = gender self.suid = suid self.course = course def choose(self): print(f"{self.name}正在選課") class Teacher(School): def __init__(self,name,age,gender,salary,level): self.name = name self.age = age self.gender = gender self.salary = salary self.level = level def score(self,student,num): student.num = num s1 = Student('alen',66,'male',10001,'python開發') t1 = Teacher('eg',18,'male',2000,10) print(t1.scholl) # 東京校區 print(s1.__dict__) # {'name': 'alen', 'age': 66, 'gender': 'male', 'suid': 10001, 'course': 'python開發'} print(t1.__dict__) # {'name': 'eg', 'age': 18, 'gender': 'male', 'salary': 2000, 'level': 10}
二:如何在子類中派生新方法重用父類的功能
方式一:指名道姓地呼叫某一個類的函式
特點:可以不以依賴繼承關係
示列
class School: scholl = '東京校區' def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender def t1(self): print("這是一個測試繼承的函式") class Student(School): def __init__(self,name,age,gender,suid,course): School.__init__(self,name,age,gender) self.suid = suid self.course = course def choose(self): print(f"{self.name}正在選課") class Teacher(School): def __init__(self,name,age,gender,salary,level): School.__init__(self,name,age,gender) self.salary = salary self.level = level def score(self,student,num): School.t1(self) student.num = num s1 = Student('alen',66,'male',10001,'python開發') t1 = Teacher('eg',18,'male',2000,10) print(s1.__dict__) # {'name': 'alen', 'age': 66, 'gender': 'male', 'suid': 10001, 'course': 'python開發'} print(t1.__dict__) # {'name': 'eg', 'age': 18, 'gender': 'male', 'salary': 2000, 'level': 10}
三:屬性查詢
在python中如果物件內部沒有屬性,就去類裡去找,如果類裡沒有就去父類找(如果沒有有繼承,就找不到報錯)
在python3中會去object裡去找,(如果沒有預設繼承,預設就是繼承object類),在python2中,存在兩種類(新式類和經典類),兩種類的查詢循序有點區別
示列
class Foo:
def f2(self):
print("from foo.f2")
def f1(self):
print("from foo.f1")
self.f2()
class Bar(Foo):
def f2(self):
print("from Bar.f2")
t = Bar()
t.f1()
"""
from foo.f1
from Bar.f2
"""
父類如果不想讓子類覆蓋自己的方法,可以在方法名前加__,讓其成為私有屬性
class Foo:
def __f2(self):
print("from foo.f2")
def f1(self):
print("from foo.f1")
self.__f2()
class Bar(Foo):
def __f2(self):
print("from Bar.f2")
obj = Bar()
obj.f1()
```
from foo.f1
from foo.f2
```
四:繼承的原理
基礎知識
新式類:凡是繼承了object的子類,以該子類子子孫孫類都稱之為新式類
經典類:沒有繼承object的子類,以該子類子子孫孫類都稱之為經典類
在python3中全都是新式類,python2中有經典類
在python3中沒有繼承任何類,預設基礎obj類
在py3中
class Foo:
pass
print(Foo.__bases__) # (<class 'object'>,)
在py2中
class Foo:
pass
print(Foo.__bases__) # 為空
繼承的實現原理
1:菱形問題
大多數面嚮物件語言都不支援多繼承,在python中,一個子類是可以同時繼承多個父類的,這個可以讓一個子類,可以對多個不同父類加以重用的好處,但是也有可能引發著名的菱形問題(或稱死亡鑽石問題)
如圖
![菱形繼承](D:\Program Files (x86)\markdown\python3.8\菱形繼承.png)
A類在頂部,B類和C類分別位於其下方,D類在底部將兩者連線在一起形成菱形。
這個圖展示的就是菱形繼承
如果A中有一個方法,B或者C都重寫了該方法,而D沒有重寫,那麼D繼承的是哪個版本的方法:是B或者是C?看下面
在py3中
class A:
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,C):
pass
obj = D
obj.test() # 結果為from B
要想知道obj.test()是如何找到方法test的,需要了解Python的繼承實現原理--
2繼承原理
MRO
Python是如何實現繼承的呢?
對於你定義的每一個類,python都會計算出方法解析順(mro)列表,該mro列表就是一個簡單的所有基類的線性順序列表,如下
print(D.mro())
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
python會在mro列表上從左到右開始查詢基類,直到找到一個第一匹配這個屬性的類為止
這個mro列表的構造是通過一個C3線性演算法來實現的。
這個演算法實際上就是合併所有父類的mro列表並遵循如下原則
1:子類會先於父類查詢
2:多個父類會根據它們所在列表中的順序被檢查
3:如果對下一個類存在兩個合法的選擇,選擇第一個父類
屬性查詢
1:由物件發起的屬性查詢,會從物件自身的屬性裡檢索,沒有則會按照物件的類的mro列表規定的順序依次找下去
2:由類發起的屬性查詢,會按照當前類.mro規定的順序依次找下去
示列
#【test1】
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(C,B):
def test(self):
print('from D')
print(D.mro()) # 類D以及類D的物件訪問屬性都是參照該類的mro列表
# 輸出結果:
# [<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
obj1 = D()
obj1.test() # 輸出結果:from D
print(D.test) # 輸出結果:<function D.test at 0x0000012EA27FA4C0>
print(C.mro()) # 類C以及類C的物件訪問屬性都是參照該類的mro列表
# 輸出結果:[<class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
obj2 = C()
obj2.test() # 輸出結果:from C
print(C.test) # 輸出結果: <function C.test at 0x0000022FA7D0A3A0>
# 【test2】
class A(object):
# def test(self):
# print('from A')
pass
class B(A):
def test(self):
print('from B')
class C(A):
# def test(self):
# print('from C')
pass
class D(C,B):
# def test(self):
# print('from D')
pass
print(D.mro()) # 類D以及類D的物件訪問屬性都是參照該類的mro列表
# 輸出結果:
# [<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
obj1 = D()
obj1.test() # 輸出結果:from B
print(D.test) # 輸出結果:<function B.test at 0x00000246D6F3A310>
# 總結:類相關的屬性查詢(類名.屬性,該類的物件.屬性),都是參照該類的mro
3:深度優先和廣度優先
1:非菱形繼承
如果多繼承是非菱形繼承,經典類與新式類的屬性查詢順序一樣:都是一個分支一個分支地找下去,然後找object類
示列如下
![image-20200807201837419](D:\Program Files (x86)\markdown\python3.8\image-20200807201837419.png)
程式碼演示
class E:
# def test(self):
# print('from E')
pass
class F:
def test(self):
print('from F')
class B(E):
# def test(self):
# print('from B')
pass
class C(F):
# def test(self):
# print('from C')
pass
class D:
def test(self):
print('from D')
class A(B, C, D):
# def test(self):
# print('from A')
pass
# 新式類
print(A.mro())
# A->B->E->C->F->D->object
# [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>,
# <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class 'object'>]
obj = A()
obj.test() # 結果為:from F
2:菱形繼承
如果多繼承是菱形繼承,經典類與新式類的屬性查詢順序不一樣
經典類:深度優先,會在在檢索第一個分支的時候就直接一天道走到黑,即會檢索(共同的父類)
新式類:廣度優先,會在檢索最後一條分支的時候檢索共同的父類
![image-20200807202406380](D:\Program Files (x86)\markdown\python3.8\image-20200807202406380.png)
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,C):
pass
obj = D()
obj.test() # 結果為:from B
# 如果B沒有去找C,c沒有然後找A
python到底是如何實現繼承的呢? 對於你定義的每一個類,Python都會計算出一個方法解析順序(MRO)列表,該MRO列表就是一個簡單的所有基類的線性順序列表,如下
print(d.mro) # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
class G: # 在python2中,未繼承object的類及其子類,都是經典類
# def test(self):
# print('from G')
pass
class E(G):
# def test(self):
# print('from E')
pass
class F(G):
def test(self):
print('from F')
class B(E):
# def test(self):
# print('from B')
pass
class C(F):
def test(self):
print('from C')
class D(G):
def test(self):
print('from D')
class A(B,C,D):
# def test(self):
# print('from A')
pass
# # 新式類
print(A.mro())
# # A->B->E->C->F->D->G->object
# # [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>,
# # <class '__main__.D'>, <class '__main__.G'>, <class 'object'>]
obj = A()
obj.test() # 輸出結果:from C
# 經典類:A->B->E->G->C->F->D
print(A.mro())
obj = A()
obj.test() # 輸出結果:from C
3:總結
多繼承到底要不要用?
python既然提供了,那麼就要用,但是要規避問題
1:繼承結構儘量不要過於複雜
2:推薦使用mixins機制:在多繼承的背景下滿足繼承是什麼關係
四:python Mixins機制
用來解決多繼承所帶來的問題的,將原本的混亂的繼承清晰的變成’is-a‘的關係
# 民航飛機、直升飛機、汽車都是交通工具,但是飛機會飛,汽車不會,
# 這個時候我們將飛這個功能放到交通工具中就顯得不合適
class Vehicle: # 交通工具
def fly(self):
'''
飛行功能相應的程式碼
'''
print("I am flying")
class CivilAircraft(Vehicle): # 民航飛機
pass
class Helicopter(Vehicle): # 直升飛機
pass
class Car(Vehicle): # 汽車並不會飛,但按照上述繼承關係,汽車也能飛了
pass
# 針對上面的問題,就引入Mixin機制
# Mixin機制指的就是子類混合不同類的功能,然後將其用統一的命名規範來標識該類只是用來混合功能的
# 從而來遵守多繼承下的“is-a”關係,Mixin機制本質仍是多繼承
class Vehicle: # 交通工具
pass
class FlyMixin:
def fly(self):
'''
飛行功能相應的程式碼
'''
print("I am flying")
class CivilAircraft(FlyMixin,Vehicle): # 民航飛機
pass
class Helicopter(FlyMixin,Vehicle): # 直升飛機
pass
class Car(Vehicle): # 汽車並不會飛,但按照上述繼承關係,汽車也能飛了
pass
'''
使用Mixin類實現多重繼承要非常小心
1、它必須表示某一種功能,而非物品,python 對於mixin類的命名方式一般以 Mixin, able, ible 為字尾
2、它必須責任單一,如果有多個功能,那就寫多個Mixin類,一個類可以繼承多個Mixin,為了保證遵循繼承
的“is-a”原則,只能繼承一個標識其歸屬含義的父類
3、然後,它不依賴於子類的實現
4、最後,子類即便沒有繼承這個Mixin類,也照樣可以工作,就是缺少了某個功能。
(比如飛機照樣可以載客,就是不能飛了)
'''
五:派生與方法重用
-
子類中衍生出的新東西
- 子類獨有,父類沒有
- 子類有,父類也有,子類是完全覆蓋父類
- 子類有,父類也有,子類在父類的基礎上進行擴充套件
class People:
... school='清華大學'
...
... def init(self,name,sex,age):
... self.name=name
... self.sex=sex
... self.age=age
...class Teacher(People):
... def init(self,name,sex,age,title): # 派生
... self.name=name
... self.sex=sex
... self.age=age
... self.title=title
... def teach(self):
... print('%s is teaching' %self.name)
```
-
想在子類派生出的方法內重用父類的功能,有兩種實現方式
# 方法一:“指名道姓”地呼叫某一個類的函式=》不依賴於繼承 >>> class Teacher(People): ... def __init__(self,name,sex,age,title): ... People.__init__(self,name,age,sex) #呼叫的是函式,因而需要傳入self ... self.title=title ... def teach(self): ... print('%s is teaching' %self.name) # 方法二:super()呼叫父類提供給自己的方法=》嚴格依賴繼承關係 # 呼叫super()會得到一個特殊的物件,該物件會按照發起屬性查詢的類的mro去當前類的父類中找屬性 >>> class Teacher(People): ... def __init__(self,name,sex,age,title): ... super().__init__(name,age,sex) #呼叫的是繫結方法,自動傳入self ... self.title=title ... def teach(self): ... print('%s is teaching' %self.name) >>> #A沒有繼承B ... class A: ... def test(self): ... super().test() ... >>> class B: ... def test(self): ... print('from B') ... >>> class C(A,B): ... pass ... >>> C.mro() # 在程式碼層面A並不是B的子類,但從MRO列表來看,屬性查詢時,就是按照順序C->A->B->object,B就相當於A的“父類” [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>,<class ‘object'>] >>> obj=C() >>> obj.test() # 屬性查詢的發起者是類C的物件obj,所以中途發生的屬性查詢都是參照C.mro() from B
六:組合
什麼是組合?
- 一個類中以另外一個類的物件作為資料屬性,作為類的組合
# 組合與繼承都是用來解決程式碼的重用性問題。 # 二者的區別在於 ''' 繼承:強調‘是’的關係,例如老師是人、學生是人,當類之間有很多相同的之處,應該使用繼承 組合:強調‘有’的關係,例如老師有多門課程,老師有給學生打分,當類之間有顯著不同,並且較小的類是較大的類所需 要的元件時,應該使用組合 ''' 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 Date: def __init__(self,year,mon,day): self.year=year self.mon=mon self.day=day def tell_birth(self): print('<%s-%s-%s>' %(self.year,self.mon,self.day)) class People: school='清華大學' def __init__(self,name,sex,age): self.name=name self.sex=sex self.age=age #Teacher類基於繼承來重用People的程式碼,基於組合來重用Date類和Course類的程式碼 class Teacher(People): #老師是人 def __init__(self,name,sex,age,title,year,mon,day): super().__init__(name,age,sex) self.birth=Date(year,mon,day) #老師有生日 self.courses=[] #老師有課程,可以在例項化後,往該列表中新增Course類的物件 def teach(self): print('%s is teaching' %self.name) python=Course('python','3mons',3000.0) linux=Course('linux','5mons',5000.0) teacher1=Teacher('lili','female',28,'博士生導師',1990,3,23) # teacher1有兩門課程 teacher1.courses.append(python) teacher1.courses.append(linux) # 重用Date類的功能 teacher1.birth.tell_birth() # 重用Course類的功能 for obj in teacher1.courses: obj.tell_info()
參考