Python入門筆記—第七章【面向對象之OOP(第二部分,封裝&繼承)】

第七章:面向對象之OOP(第二部分,封裝&繼承)


2 面向對象的三大特性

2.1 封裝

- 作用:對對象成員進行有限制的訪問

- 3個級別

    - 公有成員,public

    - 受保護成員,protected

    - 私有成員,private

注:public,protected,private不是關鍵字,但是變量命名等儘量避開

- Python中下劃線的使用(參考:https://blog.csdn.net/g11d111/article/details/71367649

    - object  #public(公有成員)

    - _object   #obey python coding convention, consider it as private(將其狹義的看做保護成員)

    - __object   #private(私有成員)

    - __object__   #special, python system use, user should not define like it

    (特殊成員,與公私有性質無關,如__init__表示構造函數,__doc__等)

其實在Python中並不存在保護成員的概念,如_object這樣狹義上的保護成員在子類以及外部都可以進行訪問。儘管可以在外部訪問,但是需要把他看做私有成員,儘量不要在外部訪問。

以雙下劃線開頭是Python中最高級別的封裝,即私有成員,只有類對象本身可以訪問該成員。但是Python中private的私有並不是真的私有,而是一種name mangling的改名策略,使用【對象名._類名__私有成員名】即可訪問

封裝案例:

class Student():
    name = "xiaoming"
    _age = 18
    __school = "NCU"

s = Student()
print(s.name)        #訪問公有成員
print(s._age)        #此處雖然可以訪問,但是要將其看做私有成員
#print(s.__school)    #在外部不能訪問私有成員,此處會報錯
print("*" * 20)
 
print(Student.__dict__)  #查看類Student中的成員
s._Student__school = 19  #使用name mangling可以訪問、修改私有成員,但是最好不要這樣用
print(s._Student__school) 

結果:

xiaoming
18
********************
{'__module__': '__main__', 'name': 'xiaoming', '_age': 18, '_Student__school': 'NCU', '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}
19

2.2 繼承

- 概念:一個類可以獲得另一個類的成員屬性和成員方法

- 作用:可以減少重複性的代碼,增加類代碼的服用功能,增強類與類之間的聯繫

- 繼承與被繼承的概念

    - 被繼承的類稱爲父類,也叫基類或者超類

    - 繼承的類稱爲子類,也叫派生類

    - 繼承和被繼承之間存在着is-a的關係,即

繼承的語法:參見下列代碼

#在Python中所有的類都有一個父類,即object

class Person():
    name = "xiaoming"
    age  = 18
    _petname = "mingming"  #
    __score  = 0
    def sleep(self):
        print("sleeping")
class Student(Person):         #類Student繼承父類Person,父類名Person寫在子類括號中
    school_id = "9527"         
    def homework(self):
        print("I must do homework")

s = Student()
print(s.name)          #用子類訪問父類中的成員
print(s._petname)      #用子類訪問父類中的保護成員,可以訪問但最好不要使用,將其看做私有成員
#print(s.__score)      #用子類訪問父類中的私有成員,此處會報錯
print(s.sleep())       #用子類訪問父類中的方法
print("*" * 20)        #分割線
print(s.school_id)     #子類訪問自己的成員屬性
print(s.homework())    #子類訪問自己的成員方法

結果:

xiaoming
mingming
sleeping
None
********************
9527
I must do homework
None

- 繼承的特徵

    - 所有的類都繼承自類object,即類object是所有類的父類

    - 子類可以使用父類除私有成員之外的所有內容

    - 子類在繼承父類後並沒有將父類成員全部傳入子類當中,即沒有另外開闢存儲空間,而是通過引用關係訪問(代碼示例1)

    - 子類可以定義子類獨有的屬性和方法,也即增加代碼的功能性(代碼示例1)

    - 子類和父類中的成員相同,則優先使用子類中的成員(代碼示例1)

    - 子類如果想擴充父類的方法,可以在定義新方法的同時訪問父類成員進行代碼重用。有下列兩種方法:(代碼示例2)

        - 父類名.父類成員

        - super().父類成員

代碼實例1:

#在Python中所有的類都有一個父類,即object

class Person():
    name = "xiaoming"
    age  = 18
    _petname = "mingming"  #
    __score  = 0
    def sleep(self):
        print("sleeping")
class Student(Person):         #類Student繼承父類Person,父類名Person寫在子類括號中
    school_id = 9527           #此school_id爲子類Student獨有的屬性
    name = "DaNa"

print(Person.__dict__)
print(Student.__dict__)
print("*" * 20)
s = Student()
print(s.name)                  #子類和父類擁有相同的成員,則優先使用子類中的成員

結果:

{'__module__': '__main__', 'name': 'xiaoming', 'age': 18, '_petname': 'mingming', '_Person__score': 0, 'sleep': <function Person.sleep at 0x0000000002813488>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
{'__module__': '__main__', 'school_id': 9527, 'name': 'DaNa', '__doc__': None}     
********************       #由此處可以看出,父類並沒有把父類中的成員全部到子類中,而是通過子類引用的方法來訪問父類中的成員
DaNa

代碼實例2:

class Person():
    name = "xiaona"
    age  = 19
    def sleep(self):
        print("sleeping……………")
    def work(self):
        print("make some money")
class Teacher(Person):
    name = "dana"
    age  = 21
    def test(self):
        print("make the test")
    def work(self):    
        Person.work(self)        #擴充父類功能的方法1
        #super().work()          #擴充父類功能的方法2,super表示得到父類
        self.test()

t = Teacher()
t.work()

結果:

make some money
make the test

- 關於super:

    - super - super不是關鍵字, 而是一個類

    - super的作用是獲取MRO(MethodResolustionOrder)列表中的第一個類

    - super於父類直接沒任何實質性關係,但通過super可以調用到父類

    - super使用兩個方,參見在構造函數中調用父類的構造函數

繼承變量函數的查找順序問題:

- 優先查找自己的變量,沒有則往上查找父類

- 構造函數如果子類中沒有定義,則往上查找父類;如果子類中有定義,則不再往上查找父類

2.3 構造函數

詳解見Python入門筆記—番外篇【面向對象之構造函數】

2.4 單繼承與多繼承

- 單繼承(上述關於繼承的案例都是單繼承

    - 每個類只能繼承一個類

    - 優點:傳承有序,語法簡單,邏輯清晰,隱患少

    - 缺點:功能不能無限擴展,只能侷限於當前繼承鏈中的功能

- 多繼承

    - 每個類允許繼承多個類

    - 優點:類的功能擴展方便

    - 缺點:繼承關係複雜,容易出錯

代碼實例3:

#多繼承代碼
class Person():
    def __init__(self,name):
        self.name = name
    def work(self):
        print("Working")

class Bird():
    def __init__(self,name):
        self.name = name
    def fly(self):
        print("Flying")

class Fish():
    def __init__(self,name):
        self.name = name
    def swim(self):
        print("Swimming")

#單繼承
class Student(Person):
    def __init__(self,name):
        self.name = name
stu = Student("yueyue")
stu.work()

#多繼承
class SwimMan(Person,Fish):
    def __init__(self,name):
        self.name = name
swi = SwimMan("yueyue")
print("*" * 20)
swi.work()
swi.swim()

#多繼承
class SuperMan(Person,Bird,Fish):
    def __init__(self,name):
        self.name = name
sup = SuperMan("yueyue")
print("*" * 20)
sup.work()
sup.fly()
sup.swim()

結果:

Working
********************
Working
Swimming
********************
Working
Flying
Swimming

2.4.1 菱形繼承/鑽石繼承問題

- 多個子類繼承來自同一個父類,這些子類又被同一個類繼承,形成一個菱形(鑽石)的形狀,如下圖

                                                               

#菱形問題代碼
class A():
    pass

class B(A):
    pass

class C(A):
    pass

class A(B,C):
    pass

- 對於解決菱形問題,一般採用MRO方法(Method Resolution Order,MRO)

- MRO具體參考:https://www.cnblogs.com/whatisfantasy/p/6046991.html

- Python 3中採用MRO C3的方法來解決菱形問題(上面鏈接有詳解)

- MRO列表計算原則:

    - 子類永遠在父類前面

    - 如果有多個父類,則根據繼承語法中括號內類的書寫順序存放

    - 如果多個類繼承了同一個父類,則孫子類只會選取繼承語法括號中的第一個父親的父類?

  

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章