面向對象階段最重要的知識點:
面向對象的三大特性
- 繼承(組合)
- 封裝
- 多態
繼承(靈魂三拷問)
什么是繼承?
繼承指的是定義類的方法,新定義的類稱之為子類或者派生類
子類繼承的類叫做父類,也叫基類/超類
繼承的特性:
- 子類可以繼承父類的屬性(特征與技能)
- 並且可以派生出自己的屬性(特征和技能)
- 在python中一個子類可以繼承多個父類,其他語言只能繼承一個父類
為什么要繼承
減少代碼的冗余(減少重復代碼)。
如何實現繼承
-
首先確定好誰是子類,誰是父類
-
在定義類時,子類(),()內寫上父類名,實現繼承
繼承初體驗:
# 父類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)
繼承下對象屬性查找順序
在繼承背景下,對象屬性的查找順序為:
-
對象查找屬性會先從對象的名稱呢過空間中查找
-
若對象中沒有,則會去類里面查找
-
若當前子類里面有就返回,如果沒有會去父類里面找
注意:若子類定義和父類相同的屬性,會優先使用子類的。
# 驗證對象屬性的查找順序:
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.在多繼承的情況下形成的鑽石繼承 (繼承順序)
- 經典類:
深度優先
- 新式類:
廣度優先