iT邦幫忙

2022 iThome 鐵人賽

DAY 8
0
Software Development

燃燒大三的成果發表系列 第 8

燃燒大三的成果發表第八天 - 繼承、多型、抽象

  • 分享至 

  • xImage
  •  

我們在昨日了解封裝之後,今日我們要延伸到繼承與多型,以及抽象化

繼承

繼承的核心概念非常的簡單,就是父類別有的屬性和方法能夠接著做使用,可以重複利用程式碼,架構上也會比較完整且直觀。

讓我們來看一下範例程式碼:

class Father(object):

    last_name = "Lin"

    def __init__(self, name, height, weight, age):
        self.__name = name
        self.height = height
        self.weight = weight
        self.age = age

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = "new_name " + name

    @staticmethod
    def tool():
        status = "打開"
        return "{status} 麥克風".format(status=status)

    def introduce_myself(self):
        tool = Father.tool()
        print("{tool} 説: 我叫 {name} 身高{height}cm 體重{weight}kg 年齡{age} years old".format(tool=tool, name=self.name, height=self.height, weight=self.weight, age=self.age))


class Child(Father):

    def __init__(self, name, height, weight, age):
        super().__init__(name, height, weight, age)
        self.friend = "Sam"

    def introduce_myself(self):
        print("我是 {name},我有朋友 {friend}".format(name=self.name, friend=self.friend))


if __name__ == '__main__':
    Jacky = Father("Jacky", 180, 70, 58)
    Jacky.introduce_myself()  # 打開 麥克風 説: 我叫 Jacky 身高180cm 體重70kg 年齡58 years old
    son = Child("Jacky's son", 200, 80, 18)
    son.introduce_myself()  # 我是 Jacky's son,我有朋友 Sam

從程式碼中我們可以發現,我們這邊有個super.__init__的功用是呼叫父類別的__init__(建構子)非常重要,也因為我們能夠呼叫父類別的建構子,所以我們能夠繼承並使用父類別的屬性,這樣能夠延伸、擴增架構,屬性方法也不需要重複寫。

以下面的程式碼為例:

class Father(object):

    last_name = "Lin"

    def __init__(self, name, height, weight, age):
        self.__name = name
        self.height = height
        self.weight = weight
        self.age = age

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self, name):
        self.__name = "new_name " + name

    @staticmethod
    def tool():
        status = "打開"
        return "{status} 麥克風".format(status=status)

    def introduce_myself(self):
        tool = Father.tool()
        print("{tool} 説: 我叫 {name} 身高{height}cm 體重{weight}kg 年齡{age} years old".format(tool=tool, name=self.name, height=self.height, weight=self.weight, age=self.age))


class Child(Father):

    def __init__(self, name, height, weight, age):
        # super().__init__(name, height, weight, age)
        self.friend = "Sam"

    def introduce_myself(self):  # 複寫
        print("我是 {name},我有朋友 {friend}".format(name=self.name, friend=self.friend))


if __name__ == '__main__':
    Jacky = Father("Jacky", 180, 70, 58)
    Jacky.introduce_myself()  # 打開 麥克風 説: 我叫 Jacky 身高180cm 體重70kg 年齡58 years old
    son = Child("Jacky's son", 200, 80, 18)
    son.introduce_myself()  # AttributeError: 'Child' object has no attribute '_Father__name'

如果把32行註解掉,則會出現AttributeError: 'Child' object has no attribute '_Father__name'的錯誤,所以一定要呼叫父類別的建構子,才能夠繼承父類別的實體屬性。

多型

接著要來分享多型,那什麼是多型呢?多型分成「Override覆寫」、「Overload多載」。Override是因為繼承的關係,父類別與子類別有著兩個相同名稱的方法,但詳細做的事情不相同;Overload是在同一個類別底下,有著多個相同名稱的方法,但引入參數的型態各不同,那雖然有分這兩種用法,但是python裡沒有多載,在python裡,如果在同個類別底下宣告同樣名稱的方法,即使引入參數型態不同一樣會覆蓋前面的方法。

那我在24行和35行的地方有用到Override,雖然方法名稱一樣,但是在子類別下的方法可能做著更詳細的事情,像是不同人做同樣的事情,可是每個人詳細做法不同,大概是這樣一個概念。

抽象化

最後我們來提一下抽象化,抽象化就是先寫出一個「架構」、「藍圖」、「設計圖」能夠讓人照著實作

我們來看看範例程式碼:

from abc import ABC, abstractmethod, abstractproperty

class Ancestor(ABC):

    last_name = ""
    name = ""
    height = 0
    age = 0

    @staticmethod
    @abstractmethod
    def tool():
        pass

    @abstractmethod
    def introduce_myself(self):
        pass


class Father(Ancestor):

    last_name = "Lin"

    def __init__(self, name, height, weight, age):
        self.Ancestor__name = name
        self.height = height
        self.weight = weight
        self.age = age
        
    @property
    def name(self):
        return self._Ancestor__name

    @name.setter
    def name(self):
        return "new_name " + self._Ancestor__name

    @staticmethod
    def tool():
        status = "打開"
        return "{status} 麥克風".format(status=status)

    def introduce_myself(self):
        tool = Father.tool()
        print("{tool} 説: 我叫 {name} 身高{height}cm 體重{weight}kg 年齡{age} years old".format(tool=tool, name=self.name, height=self.height, weight=self.weight, age=self.age))


if __name__ == '__main__':
    Jacky = Father("Jacky", 180, 70, 58)
    Jacky.introduce_myself()  # 打開 麥克風 説: 我叫 Jacky 身高180cm 體重70kg 年齡58 years old

20-50行是前面繼承的例子,我在1-19行加上了一個抽象類別,透過抽象類別這樣一個介面、模組,讓Father這個Class去實作,以上面的程式碼為例,繼承抽象類別的子類別沒有實作introduce_myself方法的話,就會報TypeError: Can't instantiate abstract class Father with abstract methods introduce_myself的錯誤。

這裡我在實作的時候遇到一些小小的狀況,記錄一下,就是我原本想在抽象設定寫建構子,宣告私有屬性,但是後來想到因為抽象類別本身就是設計給人繼承的,並「不會實例化」更不會有「建構子」,也就不會有「實例屬性」,再來是要給人繼承也不會宣告「私有屬性」,雖然在python底下還是可以使用_class__attribute這樣的方式去呼叫私有屬性,但是這是不對的,一般來說私有屬性是除了本身自己類別以外都無法使用,所以後來我全部改成使用「類別屬性且非私有」來寫。

特性 封裝 繼承 多型 抽象
敘述 將詳細的實作內容以及資訊隱藏,限制特定類別存取屬性,避免該屬性被修改 將程式碼封裝後,透過繼承父類別的屬性,不但可以延伸、擴增架構,屬性與方法也不需要重複寫 繼承之後我們可以透過「覆寫」的用法,或者是「多個類別相同名稱的方法」,可以表達出每個「不同類別但同樣名稱的方法」實作的詳細內容 寫出一個「架構」、「藍圖」、「設計圖」能夠讓人照著實作
注意事項 限制存取屬性可以使用property super可以呼叫父類別的屬性,沒有super.__init__的話,子類別依然可以繼承並使用父類別屬性,要注意繼承屬性的權限(public/ protected/ private) 多型有「覆寫」、「多載」python不支援多載 不會有建構子,也就不會有實例屬性,不會有私有屬性

物件導向的特性就分享到這邊告一段落,明天我要接著要來介紹封裝


上一篇
燃燒大三的成果發表第七天 - 屬性(Attributes)
下一篇
燃燒大三的成果發表第九天 - 封裝
系列文
燃燒大三的成果發表30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言