第十一章 (補充)面向物件繼承
阿新 • • 發佈:2021-12-07
目錄
繼承
繼承簡介
- 繼承是一種建立新類的方式,新建的類可稱為子類或派生類,父類可稱為基類或超類
- 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機制
- 繼承表達的是一種“是”什麼關係
Mixins機制
- 多繼承的正確開啟方式:mixins機制
- mixins機制核心:就是在多繼承背景下儘可能底提升多繼承的可讀性
- 讓多繼承滿足人的思維習慣--->什麼“是”什麼
class Vehicle:
pass
class FlyableMixin: # 規範多繼承
def fly(self):
pass
# 民航飛機
class CiviAircraft(FlyableMixin,Vehicle)
pass
#直升機
class Helicopter(FlyableMixin,Vehicle)
pass
class Car(Vehicle)
pass
'''表達是的關係,放在繼承的末尾,特們都是交通工具'''
'''eg:飛機有飛的功能,是交通工具'''
如果有多個功能就必須寫多個Mixin類!
繼承的查詢順序
- 物件>子類>父類>父父類
- 單繼承背景下屬性查詢
示例如下:
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
抽象類
python的抽象類需要藉助模組實現,抽象類是一個特殊的類,它只能被繼承,不能被例項化
- 作用:在不同的模組中通過抽象基類來呼叫,可以用最精簡的方式展示出程式碼之間的邏輯關係,讓模組之間的依賴清晰簡單,使得程式碼的可讀性變高。
- 注意:子類繼承抽象類的時候,必須定義相同方法對抽象類的方法進行覆蓋
import abc
# 抽象類: 抽象類只能被繼承,不能被例項化
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod # 該方法已經是抽象方法了
def speak(self): pass
@abc.abstractmethod
def login(self):pass
class People(Animal):
def speak(self):
print('嗷嗷嗷')
def login(self):
pass
class Pig(Animal):
def speak(self):
print('哼哼哼')
class Dog(Animal):
def speak(self):
print('汪汪汪')
obj = People()
obj.speak()
方法補充:
-
sel.__class__
檢視物件所屬類 -
類名/物件名.__dict__
檢視類/物件名稱空間 -
類名/物件名.__bases__
檢視父類 -
起始類名.__mro__
列印繼承順序,py3從左到右查詢 -
locals()
檢視區域性名稱空間 -
globals()
檢視全域性名稱空間 -
dirs(str)
檢視字串所搭配的內建方法有哪些,檢視內容可換