1. 程式人生 > >面向對象封裝、繼承、多態

面向對象封裝、繼承、多態

override 傳遞 實現 開發 並且 存在 dog spa 特殊

一、面向對象封裝

01. 封裝

  1. 封裝 是面向對象編程的一大特點

  2. 面向對象編程的 第一步 —— 將 屬性方法 封裝 到一個抽象的

  3. 外界 使用 創建 對象,然後 讓對象調用方法

  4. 對象方法的細節 都被 封裝類的內部

02. 小明跑步案例

  需求:

  1. 小明 體重 50公斤

  2. 小明每次 跑步 會減肥 1公斤

  3. 小明每次 吃東西 體重增加 0.5 公斤

  class Person(object):
  
      def __init__(self, name, weight):
          self.name 
= name self.weight = weight def __str__(self): return %s 體重 %.2f 公斤 % (self.name, self.weight) def run(self): print(%s 正在跑步 % self.name) self.weight -= 1 def eat(self): print(%s 正在吃東西 % self.name) self.weight
+= 0.5 xiaoming = Person(xiaoming, 50) print(xiaoming) xiaoming.run() xiaoming.eat() print(xiaoming)

03. 擺放家具案例

  需求

  1. 房子(House)戶型總面積家具名稱列表

    • 新房子沒有任何的家具

  2. 家具(HouseItem)名字占地面積,其中

    • 席夢思(bed) 占地 4平米

    • 衣櫃(chest) 占地 2平米

    • 餐桌(table) 占地 1.5平米

  3. 將以上三件 家具 添加房子

  4. 打印房子時,要求輸出:戶型總面積剩余面積家具名稱列表

剩余面積

  1. 在創建房子對象時,定義一個 剩余面積的屬性初始值和總面積相等

  2. 當調用 add_item方法,向房間 添加家具 時,讓 剩余面積 -= 家具面積

思考:應該先開發哪一個類?

答案 —— 家具類

  1. 家具簡單

  2. 房子要使用到家具,被使用的類,通常應該先開發

class HouseItem(object):

    def __init__(self, name, area):
        # name : 家具名稱
        # area : 家具占地面積
        self.name = name
        self.area = area

    def __str__(self):
        return %s 占地 %.2f 平米 % (self.name, self.area)


bed = HouseItem("席夢思", 4)
chest = HouseItem("衣櫃", 2)
table = HouseItem("餐桌", 1.5)


#print(bed)
#print(chest)
#print(table)


class House(object):

    def __init__(self, house_type, area):
        # housetype: 房子類型
        # area: 房子總面積
        self.house_type = house_type
        self.area = area
        self.free_area = area
        self.house_item = list()

    def __str__(self):
        return 房子類型:%s,總面積:%.2f,剩余可用面積:%.2f,家具:%s % (s
elf.house_type, self.area, self.free_area, self.house_item)

    def add_item(self,item):
        print("要添加:%s" %item)
        if item.area > self.free_area:
            print("剩余面積不足")
            return
        else:
            self.house_item.append(item.name)
            self.free_area -= item.area


my_home = House("小別野", 200)

my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(table)

print(my_home)

04.身份運算符

身份運算符用於 比較 兩個對象的 內存地址 是否一致 —— 是否是對同一個對象的引用

  • 在python中針對None比較時,建議使用 is 判斷
運算符描述實例
is is 是判斷兩個標識符是不是引用同一個對象 x is y,類似 id(x) == id(y)
is not is not 是判斷兩個標識符是不是引用不同對象 x is not y,類似 id(a) != id(b)

is 與 == 區別:

is 用於判斷 兩個變量 引用對象是否為同一個
== 用於判斷 引用變量的值 是否相等

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> b is a 
False
>>> b == a
True

二、繼承

  • 單繼承

  • 多繼承

面向對象三大特性

  1. 封裝 根據 職責屬性方法 封裝 到一個抽象的

  2. 繼承 實現代碼的重用,相同的代碼不需要重復的編寫

  3. 多態 不同的對象調用相同的方法,產生不同的執行結果,增加代碼的靈活度

01. 單繼承

1.1 繼承的概念、語法和特點

繼承的概念子類 擁有 父類 的所有 方法屬性

技術分享圖片

1) 繼承的語法

class 類名(父類名):

    pass
  • 子類 繼承自 父類,可以直接 享受 父類中已經封裝好的方法,不需要再次開發

  • 子類 中應該根據 職責,封裝 子類特有的 屬性和方法

2) 專業術語

  • Dog類是 Animal類的子類,Animal類是Dog類的父類,Dog類從Animal類繼承

  • Dog類是 Animal類的派生類,Animal類是Dog類的基類,Dog類從Animal類派生

3) 繼承的傳遞性

  • C類從B類繼承,B類又從A類繼承

  • 那麽C類就具有B類和A類的所有屬性和方法

子類 擁有 父類 以及 父類的父類 中封裝的所有 屬性方法

1.2 方法的重寫

  • 子類 擁有 父類 的所有 方法屬性

  • 子類 繼承自 父類,可以直接 享受 父類中已經封裝好的方法,不需要再次開發

應用場景

  • 父類 的方法實現不能滿足子類需求時,可以對方法進行 重寫(override)

技術分享圖片

重寫 父類方法有兩種情況:

  1. 覆蓋 父類的方法
  2. 對父類方法進行 擴展

1) 覆蓋父類的方法

  • 如果在開發中,父類的方法實現子類的方法實現完全不同

  • 就可以使用 覆蓋 的方式,在子類中 重新編寫 父類的方法實現

具體的實現方式,就相當於在 子類中 定義了一個 和父類同名的方法並且實現

重寫之後,在運行時,只會調用 子類中重寫的方法,而不再會調用 父類封裝的方法

2) 對父類方法進行擴展

  • 如果在開發中,子類的方法實現包含 父類的方法實現

    • 父類原本封裝的方法實現子類方法的一部分
  • 就可以使用 擴展 的方式

    1. 在子類中 重寫 父類的方法

    2. 在需要的位置使用 super(). 父類方法來調用父類方法的執行

    3. 代碼其他的位置針對子類的需求,編寫 子類特有的代碼實現

關於 super

  • 在Python中super是一個 特殊的類

  • super()就是使用super類創建出來的對象

  • 最常 使用的場景就是在 重寫父類方法時,調用 在父類中封裝的方法實現

調用父類方法的另外一種方式

  在Python 2.x時,如果需要調用父類的方法,還可以使用以下方式:

父類名.方法(self)
  • 這種方式,目前在Python 3.x還支持這種方式

  • 這種方法 不推薦使用,因為一旦 父類發生變化,方法調用位置的 類名 同樣需要修改

提示

  • 在開發時,父類名和super()兩種方式不要混用

  • 如果使用 當前子類名 調用方法,會形成遞歸調用,出現死循環

1.3 父類的 私有屬性 和 私有方法

  1. 子類對象 不能 在自己的方法內部,直接 訪問 父類的 私有屬性私有方法

  2. 子類對象 可以通過 父類公有方法 間接 訪問到 私有屬性私有方法

  • 私有屬性、方法 是對象的隱私,不對外公開,外界 以及 子類 都不能直接訪問

  • 私有屬性、方法 通常用於做一些內部的事情

技術分享圖片

  • B的對象不能直接訪問__num2屬性

  • B的對象不能在demo方法內訪問__num2屬性

  • B的對象可以在demo方法內,調用父類的test方法

  • 父類的test方法內部,能夠訪問__num2屬性和 __test方法

02. 多繼承

概念

  • 子類 可以擁有 多個父類,並且具有 所有父類屬性方法
  • 例如:孩子 會繼承自己 父親母親特性

技術分享圖片

語法

class 子類名(父類名1, 父類名2...)
    pass

2.1 多繼承的使用註意事項

  • 如果 不同的父類 中存在 同名的方法子類對象 在調用方法時,會調用 哪一個父類中的方法呢?

提示:開發時,應該盡量避免這種容易產生混淆的情況! —— 如果 父類之間 存在 同名的屬性或者方法,應該 盡量避免 使用多繼承

技術分享圖片

Python 中的 MRO —— 方法搜索順序

  • Python中針對 提供了一個 內置屬性 __mro__ 可以查看 方法 搜索順序

  • MRO 是method resolution order,主要用於 在多繼承時判斷 方法、屬性 的調用 路徑

print(C.__mro__)

輸出結果

(<class __main__.C>, <class __main__.A>, <class __main__.B>, <class object>)
  • 在搜索方法時,是按照 __mro__ 的輸出結果 從左至右 的順序查找的

  • 如果在當前類中 找到方法,就直接執行,不再搜索

  • 如果 沒有找到,就查找下一個類 中是否有對應的方法,如果找到,就直接執行,不再搜索

  • 如果找到最後一個類,還沒有找到方法,程序報錯

2.2 新式類與舊式(經典)類

objectPython為所有對象提供的 基類,提供有一些內置的屬性和方法,可以使用dir函數查看

  • 新式類:以object為基類的類,推薦使用

  • 經典類:不以object為基類的類,不推薦使用

  • 在Python 3.x中定義類時,如果沒有指定父類,會 默認使用 object 作為該類的 基類 ——Python 3.x中定義的類都是 新式類

  • 在Python 2.x中定義類時,如果沒有指定父類,則不會以object作為 基類

新式類 和 經典類 在多繼承時 —— 會影響到方法的搜索順序

為了保證編寫的代碼能夠同時在Python 2.x和Python 3.x運行!
今後在定義類時,如果沒有父類,建議統一繼承自object

class 類名(object):
    pass

三、多態

面向對象三大特性

  1. 封裝 根據 職責屬性方法 封裝 到一個抽象的
    • 定義類的準則

  2. 繼承 實現代碼的重用,相同的代碼不需要重復的編寫
    • 設計類的技巧

    • 子類針對自己特有的需求,編寫特定的代碼

  3. 多態 不同的 子類對象 調用相同的 父類方法,產生不同的執行結果

    • 多態 可以 增加代碼的靈活度
    • 繼承重寫父類方法 為前提
    • 是調用方法的技巧,不會影響到類的內部設計

技術分享圖片

案例

需求

  1. 在Dog類中封裝方法game

    • 普通狗只是簡單的玩耍
  2. 定義XiaoTianDog繼承自Dog,並且重寫game方法

    • 哮天犬需要在天上玩耍
  3. 定義Person類,並且封裝一個 和狗玩 的方法

    • 在方法內部,直接讓 狗對象 調用game方法

技術分享圖片

案例小結

  • Person類中只需要讓 狗對象 調用game方法,而不關心具體是 什麽狗

    • game方法是在Dog父類中定義的

  • 在程序執行時,傳入不同的 狗對象 實參,就會產生不同的執行效果

  多態 更容易編寫出出通用的代碼,做出通用的編程,以適應需求的不斷變化!

class Dog(object):

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

    def game(self):

        print("%s 歡快的玩耍" % self.name)


class XiaoTianQuan(Dog):

    def game(self):
        print("%s 在天上玩耍" % self.name)


class Person(object):

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

    def game_with_dog(self, dog):
        print("%s 和 %s 愉快的玩耍" % (self.name, dog.name))
 
         # 讓狗玩耍
         dog.game()
 
 
 # 創建一個狗對象
 xiaotianquan = XiaoTianQuan("哮天犬")
 
 # 創建一個人對象
 xiaobai = Person("小白")
 
 # 小白與狗玩耍
 xiaobai.game_with_dog(xiaotianquan)

面向對象封裝、繼承、多態