繼承
繼承簡介
- 繼承是一種建立新類的方式,新建的類可稱為子類或派生類,父類可稱為基類或超類
- python支援多繼承,新建的類可以支援一個或多個父類
'''單繼承和多繼承簡單定義'''
class Parent1:
pass
class Parent2:
pass
class Sub1(Parent1): #單繼承
pass
print(Sub1.__bases__) # 檢視自己的父類---->(<class '__main__.Parent1'>,)
class Sub2(Parent1,Parent2): # 多繼承
pass
print(Sub2.__bases__) # 檢視自己的父類---->(<class '__main__.Parent1'>, <class '__main__.Parent2'>)
經典類與新式類
在py2中有經典類和新式類的區別:
新式類:繼承了object類的子類,以及該子類的子類,子子類
經典類:沒有繼承object類的子類,以及該子類的子類,子子類
'''py2中'''
class Foo:
pass # 經典類
class Bar(object):
pass # 新式類
注意:在py3中沒有繼承任何類,預設繼承object類,所以python3中都是新式類
'''py3中'''
class Foo():
pass
print(Foo.__bases__) # --->(<class 'object'>,),預設繼承object類
class Sub(Foo):
pass
print(Sub.__bases__) # ---->(<class '__main__.Foo'>,)
類繼承解決了什麼問題
- 類解決物件與物件之間程式碼冗餘的問題,子類可以遺傳父類的屬性
- 繼承解決的是類與類之間程式碼冗餘的問題
- object類豐富了程式碼的功能
示例如下:
'''學生選課系統和老師打分功能'''
# 學生類
class Student():
def __init__(self,name,age,gender,course = None):
self.name = name
self.age = age
self.gender = gender
self.course = course
# 定義一個選課的方法
def choose_course(self,course):
if self.course is None:
self.course = []
self.course.append(course)
print(f"Student choice class --->{self.course}")
# 教師類
class Teacher():
def __init__(self,name,age,gender,level):
self.name = name
self.age = age
self.gender = gender
self.level = level
# 定義一個打分方法
def make_score(self,stu_obj,score):
stu_obj.score = score
print(f'Teacher{self.name} make {stu_obj.score} to {stu_obj.name}! ')
'''有很多冗餘的程式碼,優化一下,定義一個人的類整合一下重複的程式碼'''
# 人類
class Human():
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
# 學生類
class Student(Human):
def __init__(self, name, age, gender, score=None, course=None):
Human.__init__(self, name, age, gender)
self.score = score
self.course = course
# 定義一個選課的方法
def choose_course(self, course):
if self.course is None:
self.course = []
self.course.append(course)
print(f"Student choice class --->{self.course}")
# 教師類
class Teacher(Human):
def __init__(self, name, age, gender, level):
Human.__init__(self, name, age, gender)
self.level = level
# 定義一個打分方法
def make_score(self, stu_obj, score):
stu_obj.score = score
print(f'Teacher{self.name} make {stu_obj.score}marks to {stu_obj.name}! ')
# 學生類例項化
stu = Student('HammerZe', 18, 'male')
stu.choose_course('python')
# 教師類例項化
teacher = Teacher('li', 18, 'male', 10)
teacher.make_score(stu, 90)
Student choice class --->['python']
Teacherli make 90marks to HammerZe!
多繼承的優缺點
-
優點:子類可以同時遺傳多個父類的屬性,最大限度的重用程式碼
-
缺點:違反人的思維習慣,一個人有兩個爹,程式碼的可讀性會變差,不建議使用多繼承,如果不可避免多個父類的繼承,應該使用
Mixins機制
參考 -
繼承表達的是一種“是”什麼關係
繼承的查詢順序
-
物件>子類>父類>父父類
-
單繼承背景下屬性查詢
示例如下:
class Foo():
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.f1()
class Bar(Foo):
def f1(self):
print('Bar.f1')
obj = Bar()
obj.f2()
# 結果
Foo.f2
Bar.f1
'''查詢順序:
1.obj先從obj名稱空間找,再從Bar名稱空間中找,沒有f2去他爹(Foo)中找
2.執行Foo中得f2,遇到self.f1()此時self是obj,是Bar的物件
3.執行Bar中的f1
'''
# 區別下:父類不想讓子類的方法覆蓋,可以私有化
class Foo:
def __f1(self): # _Foo__f1()
print('Foo.f1')
def f2(self):
#
print('Foo.f2')
self.__f1() # _Foo__f1()
class Bar(Foo):
def __f1(self): # # _Bar__f1()
print('Bar.f1')
obj = Bar()
obj.f2()
# 結果
Foo.f2
Foo.f1
'''Foo中f1私有化,所以輸出的是Foo中的f1'''
多繼實現原理
菱形結構
在python中可以繼承多個類,這樣就會引發下面的結構:
- 當D繼承B和C,B、C分別繼承A就會組成一個菱形的繼承關係,這樣就會涉及到查詢屬性的順序問題,A、B、C、中如果方法重名,輸出的順序是按
mro
列表輸出的順序繼承
示例如下:
'''py3中'''
class A():
def out_text(self):
print('from A')
class B(A):
def out_text(self):
print('from B')
class C(A):
def out_text(self):
print('from C')
class D(B,C):
pass
obj = D()
obj.out_text() # 結果---->from B
''' 可以列印出mro列表檢視順序'''
print(D.mro())
# [<class '__main__.D'>,
# <class '__main__.B'>,
# <class '__main__.C'>,
# <class '__main__.A'>,
# <class 'object'>]
'''這樣看來查詢順序就顯而易見了,
1、從D中找out_text方法,沒有直接去B
2、B中有out_text方法,直接輸出停止查詢'''
-
mro列表查詢準則:
-
子類先查,再查父類
-
當繼承多個父類的時候,按mro列表順序被檢查
-
如果繼承多個類,被繼承類內具有相同的方法,先輸出mro列表左邊類的方法
-
-
注意:mro列表可以寫成
__mro__
也可以,呼叫mro方法的必須是起始類,obj是D的物件,所以用D.mro() -
mro列表是通過一個C3線性演算法來實現的
非菱形結構
程式碼實現如下:
'''py3中'''
class E:
pass
class F:
pass
class B(E):
pass
class C(F):
pass
class D:
def test(self):
print('from D')
class A(B, C, D):
pass
print(A.mro())
'''
查詢順序如下:
[<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 D
深度優先和廣度優先
深度優先:
- 經典類:按深度優先查詢
經典類查詢順序如下:
在py2中,沒有繼承object的類及其子類都是經典類
程式碼實現:
'''py2中'''
class G:
def test(self):
print('from G')
class E(G):
def test(self):
print('from E')
class F(G):
def test(self):
print('from F')
class B(E):
def test(self):
print('from B')
class C(F):
def test(self):
print('from C')
class D(G):
def test(self):
print('from D')
class A(B, C, D):
pass
obj = A()
obj.test() # 查詢順序為:obj->A->B->E->G->C->F->D->object
# 結果
from B
廣度優先:
- 新式類:按廣度優先順序查詢
新式類查詢順序如下:
在py3中,預設為新式類
程式碼實現如下:
'''py3中'''
class G:
def test(self):
print('from G')
class E(G):
pass
class F(G):
pass
class B(E):
pass
class C(F):
pass
class D(G):
def test(self):
print('from D')
class A(B, C, D):
pass
obj = A()
obj.test() # 查詢順序為:obj->A->B->E->C->F->D->G->object
# 結果
from D
super()方法
super()方法的存在就是為了解決多重繼承的問題,在一個父類中使用super()方法用於呼叫下一個父類的方法
- super方法
class A:
def test(self):
print('from A')
super().test()
'''用於呼叫下一個父類的方法B.test'''
class B:
def test(self):
print('from B')
class C(A, B):
pass
c = C()
c.test()
print(C.mro())
# 查詢順序如下
#[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
# 結果
from A
from B
方法補充:
-
sel.__class__
檢視物件所屬類 -
類名/物件名.__dict__
檢視類/物件名稱空間 -
類名/物件名.__bases__
檢視父類 -
起始類名.__mro__
列印繼承順序,py3從左到右查詢 -
locals()
檢視區域性名稱空間 -
globals()
檢視全域性名稱空間 -
dirs(str)
檢視字串所搭配的內建方法有哪些,檢視內容可換