python 繼承

HammerZe發表於2021-12-06

繼承

檢視源影像

繼承簡介

  1. 繼承是一種建立新類的方式,新建的類可稱為子類或派生類,父類可稱為基類或超類
  2. 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中可以繼承多個類,這樣就會引發下面的結構:

image

  • 當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列表查詢準則:

    1. 子類先查,再查父類

    2. 當繼承多個父類的時候,按mro列表順序被檢查

    3. 如果繼承多個類,被繼承類內具有相同的方法,先輸出mro列表左邊類的方法

  • 注意:mro列表可以寫成__mro__也可以,呼叫mro方法的必須是起始類,obj是D的物件,所以用D.mro()

  • mro列表是通過一個C3線性演算法來實現的

非菱形結構

image


程式碼實現如下:

'''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

深度優先和廣度優先

深度優先:

  • 經典類:按深度優先查詢

經典類查詢順序如下:

image


在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

廣度優先:

  • 新式類:按廣度優先順序查詢

新式類查詢順序如下:

image

在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

方法補充:

  1. sel.__class__檢視物件所屬類

  2. 類名/物件名.__dict__檢視類/物件名稱空間

  3. 類名/物件名.__bases__檢視父類

  4. 起始類名.__mro__列印繼承順序,py3從左到右查詢

  5. locals()檢視區域性名稱空間

  6. globals()檢視全域性名稱空間

  7. dirs(str)檢視字串所搭配的內建方法有哪些,檢視內容可換


image


相關文章