面向對象三大特性之繼承

面向對象三大特性之繼承

一、什麼是繼承

繼承是一種新建類的方式,新建的類稱爲子類或者派生類,被繼承的類稱爲父類、基類或者超類python

在python中,一個子類能夠繼承多個父類,其餘語言中,一個子類只能繼承一個父類json

二、繼承的做用

減小類與類之間的代碼冗餘學習

子類能夠繼承父類的全部屬性,所以繼承能夠解決類與類之間的代碼冗餘問題code

三、如何實現繼承

  1. 先肯定誰是子類,誰是父類
  2. 在定義子類時語法:子類(父類):

子類能夠經過 子類.__ bases__ 查看父類對象

四、如何尋找繼承關係

肯定誰是子類blog

肯定誰是父類,先抽象再繼承繼承

​ 抽象總結出對象之間類似的部分,再總結出類字符串

​ 抽象總結類之間類似的部分,再總結出父類it

# 解決代碼冗餘
# 父類
class People:
    country = 'China'

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


# 子類1
class Student(People):
    def learn(self):
        return '學習技能'


# 子類2
class Teacher(People):
    def teach(self):
        return '教書技能'
    
# 子類能夠繼承父類的屬性
stu_obj = Student('shen', 18, 'male')
print(stu_obj.name, stu_obj.age, stu_obj.sex, stu_obj.country, stu_obj.learn()) # shen 18 male China 學習技能

tea_obj = Teacher('wang', 24, 'female')
print(tea_obj.name, tea_obj.age, tea_obj.sex, tea_obj.country, tea_obj.teach()) # wang 24 female China 教書技能

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

程序的執行是從上往下執行的,父類必須定義在子類上面class

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

  1. 先從對象自身的名稱空間中查找
  2. 再去子類的名稱空間中查找
  3. 再從父類的名稱空間中查找,最後從object中找,若沒有會報錯

python3中只有新式類,廣度優先查找:繼承多個父類時一直到繼承object的類(根父類)不會從中找會回頭找,第二個繼承的父類。。。

python2中有經典類和新式類,深度優先查找:一直找到繼承object的類(根父類)從中找不到纔會找繼承的第二個父類。。。

# 父類
class Foo:
    x = 10

#子類
class Goo(Foo):
    x = 20
    pass

obj_goo = Goo()
# 注意此處的x是經過對象來添加屬性,是給對象添加屬性,不是修改子類的屬性
obj_goo.x = 30
print(obj_goo.x)

六、派生

指的是子類繼承父類的屬性與方法,而且派生出本身獨有的屬性與方法

若子類中的方法與父類的相同,優先用子類的

# 父類
class Foo:
    def f1(self):
        print('from Foof1...')
    def f2(self):
        print('from Foof2...')
        # 注意此處查找的f1應該先優先從子類找,最後從父類找
        self.f1()
        
# 子類
class Goo(Foo):
    def f1(self):
        print('from Goof1...')

goo_obj = Goo()
goo_obj.f2() 
# from Foof2...
# from Goof1...

七、子類繼承父類並重用屬性與方法

方式一:

​ 直接引用父類的__ init__(self)爲其傳參,並添加子類的屬性

方式二:

​ 經過super來指向父類的屬性,用super().init()爲其傳參添加子類的屬性
super()是一個特殊類,調用super獲得一個對象,該對象指向父類的名稱空間

注意:使用哪種方式均可以,可是不要混用

# 需求:學生有女友屬性,老師有薪資屬性
# 父類
class Foo:
    country = 'China'

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

# 子類
class Student(Foo):
    def __init__(self, name, age, sex, gril):
        # 方式一:直接引用父類的,須要傳入self和其餘參數
        Foo.__init__(self, name, age, sex)
        self.gril = gril

class Teacher(Foo):
    def __init__(self, name, age, sex, sal):
        # 方式二:對象自己當作self傳入,不用super不要傳入self,傳入其餘參數
        super().__init__(name, age, sex)
        self.sal = sal

stu_obj = Student('shen', 18, 'male', 'gril')
print(stu_obj.country, stu_obj.name, stu_obj.age, stu_obj.sex, stu_obj.gril)
# China shen 18 male gril
tea_obj = Teacher('wang', 24, 'female', 200000)
print(tea_obj.country, tea_obj.name, tea_obj.age, tea_obj.sex, tea_obj.sal)
# China wang 24 female 200000

八、經典類與新式類

新式類:

​ 1. 凡是繼承了object的類或子孫類都是新式類

​ 2.在python3中全部的類都默認繼承了object

經典類:

​ 1. 在python2中才會有經典類與新式類之分

​ 2.在python2中,凡是沒有繼承object的類,都是經典類

# python3中
class Foo: # 默認繼承了object(Foo(object):)
    x = 1
    pass
class Goo(Foo):
    pass

print(Foo.__dict__)
print(Goo.__dict__)
# {'__module__': '__main__', 'x': 1, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
# {'__module__': '__main__', '__doc__': None}

九、mro繼承序列

python3中提供了一個查找新式類查找順序的內置方法,經過類調用mro()獲得的返回結果是一個繼承序列

super的繼承順序嚴格按照mro()繼承序列,會基於當前位置按照mro序列順序繼續日後查找

class Foo1:
    def res(self):
        print('res from Foo1')
        # super會按照mro中序列基於當前位置繼續日後查找
        super().res()

class Foo2:
    def res(self):
        print('res from Foo2')

class C(Foo1, Foo2):
    pass

obj_c = C()
print(C.mro()) # [<class '__main__.C'>, <class '__main__.Foo1'>, <class '__main__.Foo2'>, <class 'object'>]
obj_c.res()
#res from Foo1
# res from Foo2

十、鑽石繼承(菱形繼承)

多繼承狀況下的菱形繼承

mro的查找順序:

​ 新式類:廣度優先

​ 經典類:深度優先

新式類的廣度優先:

# 新式類:
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-->D-->B-->E-->C-->A-->object
print(F.mro()) #[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
obj = F()
obj.test()

經典類的深度繼承:

十一、繼承json模塊,並派生出新功能

json模塊正常沒法存入類,轉成json數據格式的,先若是須要存入一個類的數據則有兩種方式

import json
from datetime import datetime

dic = {'time': datetime.now()}
print(type(datetime.now())) # <class 'datetime.datetime'>
# datetime.now()是一個類<class 'datetime.datetime'>正常沒法轉換存成json數據格式的
json.dumps(dic) # TypeError: Object of type datetime is not JSON serializable

方法一:將類轉換成字符串存入

import json
from datetime import datetime

# 方法一:轉換成字符串存入
dic = {'time': str(datetime.now())}
res = json.dumps(dic)
print(res) # {"time": "2019-11-27 14:38:01.079162"}

方法二:繼承json模塊派生出新功能來存取

import json
from datetime import datetime

# 方法二:繼承json模塊派生出新功能來存取
class MyJson(json.JSONEncoder):
    def default(self, o):
        # isinstance: 判斷一個對象是不是一個類的實例
        if isinstance(o, datetime):
            return datetime.strftime(o, '%Y-%m-%d %X')
        else:
            return super().default(self, o)

dic = {'time': datetime.now()}
# cls=自定義的類
res = json.dumps(dic, cls=MyJson)
print(res) # {"time": "2019-11-27 14:38:01"}
相關文章
相關標籤/搜索