Python-面向對象之繼承

面向對象階段最重要的知識點:json

面向對象的三大特性

  1. 繼承(組合)
  2. 封裝
  3. 多態

繼承(靈魂三拷問)

什麼是繼承?

繼承指的是定義類的方法,新定義的類稱之爲子類或者派生類函數

子類繼承的類叫作父類,也叫基類/超類3d

繼承的特性:code

  • 子類能夠繼承父類的屬性(特徵與技能)
  • 而且能夠派生出本身的屬性(特徵和技能)
  • 在python中一個子類能夠繼承多個父類,其餘語言只能繼承一個父類

爲何要繼承

減小代碼的冗餘(減小重複代碼)。對象

如何實現繼承

  1. 首先肯定好誰是子類,誰是父類blog

  2. 在定義類時,子類(),()內寫上父類名,實現繼承繼承

    繼承初體驗:utf-8

    # 父類1
    class ParentClass1:
        pass
    
    # 父類2
    class ParentClass2:
        pass
    
    # 子類1
    class SubClass1(ParentClass1):   # 繼承父類1
        pass
    
    # 子類2
    class SubClass2(ParentClass1,ParentClass2):  # 繼承父類1,父類2
        pass
    
    # 查看繼承的父類:__bases__是類的屬性,用來查找當前類的父類
    print(SubClass1.__bases__)  # (<class '__main__.ParentClass1'>,)
    print(SubClass2.__bases__)  # (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

尋找繼承關係

如何尋找繼承關係

要想尋找繼承關係,首先要「先抽象,在繼承」開發

抽象與繼承

先抽象

抽象是一種思想

奧巴馬 --> 屬於人類 --> 屬於動物類
哈士奇 --> 屬於狗類 --> 屬於動物類

把相同的屬性(特徵和技能)抽象出來,定義動物類,稱之爲父類。

動物類:

​ 特徵:

​ 眼睛、鼻子、耳朵

​ 技能:

​ 吃、喝、拉、撒

再繼承

繼承在程序中實現

奧巴馬 (對象)--> 調用人類 --> 繼承動物類
哈士奇 (對象)--> 調用狗類 --> 繼承動物類

繼承的關係

  • 對象是特徵與技能的結合體.
  • 類是一系列對象相同的特徵與技能的結合體.
  • 繼承是一系列類相同的特徵與技能的結合體.

上代碼

# 父類
class OldboyPeople:
    # 定義相同的屬性
    school = "oldboy"
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

class OldboyTeacher(OldboyPeople):
    
    def change_score(self):
        print(f"老師{self.name} 修改分數")

class OldboyStudent(OldboyPeople):
    
    def choose_course(self):
        print(f"學生{self.name}選擇課程")

stu1 = OldboyStudent("qinyj",18,"female")
tea1 = OldboyTeacher("tank",18,"female")

print(stu1.name,stu1.age,stu1.sex)
print(tea1.name,tea1.age,tea1.sex)

繼承下對象屬性查找順序

在繼承背景下,對象屬性的查找順序爲:

  1. 對象查找屬性會先從對象的名稱呢過空間中查找

  2. 若對象中沒有,則會去類裏面查找

  3. 若當前子類裏面有就返回,若是沒有會去父類裏面找

    注意:若子類定義和父類相同的屬性,會優先使用子類的。

# 驗證對象屬性的查找順序:

class Foo:
    def f1(self):
        print("Foo.f1")

    def f2(self):
        print("Foo.f2")
        self.f1()

class Soo(Foo):
    def f1(self):
        print("Soo.f1")

s = Soo()
s.f2()
# Foo.f2
# Soo.f1
# Soo-->Foo     在Soo類中從新定義了f1方法,此時優先使用子類中的f1方法,這時候對象去子類的名稱空間裏找就會找到,打印Soo.f1

# 查看對象名稱空間  __dict__
print(s.__dict__)
# __class__:查看對象的屬性,查看當前對象的類
print(s.__class__)  # <class '__main__.Soo'>

# 查看子類名稱空間
print(s.__class__.__dict__)
# {'__module__': '__main__', 'f1': <function Soo.f1 at 0x0000000009FF3BF8>, '__doc__': None}

# 查看父類的名稱空間
# __bases__:查看繼承的父類
print(s.__class__.__bases__[0].__dict__)
# {'__module__': '__main__', 'f1': <function Foo.f1 at 0x0000000009FF3B70>, 'f2': <function Foo.f2 at 0x0000000009FF3BF8>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}

派生

什麼是派生

  • 派生指的是子類繼承父類的屬性,而且派生出新的屬性
  • 子類派生出新的屬性,若與父類屬性相同,則調用使用子類的
  • 繼承指的是類與類的關係,子類與父類是從屬的關係

子類派生新的屬性並重用父類的屬性

方式一:

直接調用父類的的__init__(self)方法,把__init__(self)當作普通函數使用,傳入對象與繼承的屬性

方式二:

使用super函數,super是一個特殊的類,在子類中調用super()會獲得一個特殊的對象,經過「.」 指向父類的名稱空間,將自己傳入__init__(self)函數當中的一個參數

注意:兩種方式不要混合使用。

class OldboyPeople:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

# 方式一:
# 直接調用父類的__init__(self)函數
class OldboyTeacher(OldboyPeople):
    def __init__(self,name,age,sex,level):
        OldboyPeople.__init__(self,name,age,sex)
        self.level = level

class OldboyStudent(OldboyPeople):
    def __init__(self,name,age,sex,course):
        OldboyPeople.__init__(self, name, age, sex)
        self.course = course

stu1 = OldboyStudent("qinyj",18,"man","python")
tea1 = OldboyTeacher("tank",18,"man","10")

print(tea1.name,tea1.level)
print(stu1.name,stu1.course)



# 方式二:
# 使用super()函數
class OldboyTeacher(OldboyPeople):
    def __init__(self,name,age,sex,level):
        super().__init__(name,age,sex)
        self.level = level

class OldboyStudent(OldboyPeople):
    def __init__(self,name,age,sex,course):
        super().__init__(name, age, sex)
        self.course = course

stu1 = OldboyStudent("qinyj",18,"man","python")
tea1 = OldboyTeacher("tank",18,"man","10")

print(tea1.name,tea1.level)
print(stu1.name,stu1.course)

新式類與經典類

繼承了 object 類的是新式類,沒有繼承的是經典類

新式類:python3中都是新式類,在python3中默認繼承object類

經典類:python2中凡是沒有繼承object類的都是經典類

mro函數

mro函數屬於object類,在多繼承狀況下,用來查看當前類的繼承順序的

class A:
    x = 2
    pass

class B:
    x = 3
    pass

class C(A,B):
    x = 1
    pass

# mro函數
print(C.mro())
# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
# 繼承順序:
# 先在本身類中找--》A--》B--》object

c = C()
print(c.x)

鑽石繼承(菱形繼承)

在多繼承的狀況下造成的鑽石繼承

針對於新式類和經典類而言:

經典類:深度優先

新式類:廣度優先

驗證:

# coding=utf-8

### 新式類繼承
# 繼承順序:F-D->B->E->->C->A->object-->若沒有報錯
class A(object):
    def test(self):
        print("from A")
    pass

class B(A):
    # def test(self):
    #     print("from B")
    pass

class C(A):
    # def test(self):
    #     print("from C")
    pass

class D(B):
    # def test(self):
    #     print("from D")
    pass

class E(C):
    # def test(self):
    #     print("from E")
    pass

class F(D,E):
    # def test(self):
    #     print("from F")
    pass

f = F()
f.test()



### 經典類繼承
# 繼承順序:F->D->B->A->E->C-->若沒有報錯
class A:
    # def test(self):
    #     print("from A")
    pass

class B(A):
    # def test(self):
    #     print("from B")
    pass

class C(A):
    # def test(self):
    #     print("from C")
    pass

class D(B):
    # def test(self):
    #     print("from D")
    pass

class E(C):
    # def test(self):
    #     print("from E")
    pass

class F(D,E):
    # def test(self):
    #     print("from F")
    pass

f = F()
f.test()

實戰-經過繼承實現修改json模塊支持的數據類型

import json
from datetime import datetime,date

print(json.JSONEncoder)
'''
json 支持的python數據類型
+-------------------+---------------+
    | Python            | JSON          |
    +===================+===============+
    | dict              | object        |
    +-------------------+---------------+
    | list, tuple       | array         |
    +-------------------+---------------+
    | str               | string        |
    +-------------------+---------------+
    | int, float        | number        |
    +-------------------+---------------+
    | True              | true          |
    +-------------------+---------------+
    | False             | false         |
    +-------------------+---------------+
    | None              | null          |
    +-------------------+---------------+
'''
print(datetime.today())
print(date.today())

# 開發者的角度來講,咱們想要把date.today() 或者datetime.today() 當成json 的value值保存爲json文件
# 但json數據類型只支持字符串,那麼咱們能夠這麼作,將執行結果強轉爲str
dic = {
    "name":"qinyj",
    "today":str(date.today())
}

print(json.dumps(dic))      # {"name": "qinyj", "today": "2019-10-10"}


# 咱們從源碼角度來看,有可能之後會本身修改源碼
class MyJson(json.JSONEncoder):
    # 子類從新派生出來default功能,優先用子類的
    def default(self, o):
        if isinstance(o,date):
            return o.strftime("%Y-%m-%d %X")
        else:
            # 不知足條件仍是繼承父類的default方法的功能
            return super().default(self,o)

dic = {
    "name":"qinyj",
    "today":date.today()
}

# isinstance() :判斷一個對象是不是一個已知的類型
print(isinstance(dic,date))
print(isinstance(dic.get("today"),date))

print(json.dumps(dic,cls=MyJson))   # 默認cls=None,默認指向的是原json的JSONEncoder
# {"name": "qinyj", "today": "2019-10-10 00:00:00"}

小結

1.什麼是繼承?
    繼承指的是新建類的方法, 新建的類稱之爲子類或者派生類,子類繼承的類叫作父類,也稱之爲基類或超類.
    
    繼承的特徵:
        子類能夠繼承父類的屬性(特徵與技能), 而且能夠派生出本身的屬性(特徵與技能).
      
2.繼承的目的:
        繼承的目的是爲了減小代碼冗餘(減小重複代碼).
    
3.什麼是抽象?
    抽象指的是抽取類似的部分,稱之爲抽象.
    
4.繼承的關係:
    對象是特徵與技能的結合體.
    類是一系列對象相同的特徵與技能的結合體.
    繼承是一系列類相同的特徵與技能的結合體.

5.在繼承背景下,對象屬性的查找順序:
    1.對象查找屬性會先從對象的名稱空間中查找.
    2.若對象沒有,則會去類裏面找.
    3.若當前類是子類,而且沒有對象找的屬性,會去父類中查找

6.什麼是派生?
    派生指的是子類繼承父類的屬性,而且派生出新的屬性.(*****)
    子類派生出新的屬性,若與父類的屬性相同,則以子類的爲準.
    繼承是誰與誰的關係, 指的是類與類的關係,子類與父類是從屬關係.

7.子類派生出新的屬性,並重用父類的屬性:
        - 直接經過 父類.(調用)__init__,把__init__當作普通函數使用,傳入對象與繼承的屬性.
        - super是一個特殊的類,在子類中調用super()會獲得一個特殊的對象,

8.什麼經典類與新式類:
        繼承object的類都稱之爲新式類.
        在python2中,凡是沒有繼承object的類都是經典類.

9.在多繼承的狀況下造成的鑽石繼承 (繼承順序)
        - 經典類:
            深度優先
        - 新式類:
            廣度優先
相關文章
相關標籤/搜索