『無為則無心』Python物件導向 — 54、重寫和super()函式

繁華似錦Fighting發表於2022-02-25

1、重寫

在子類中如果有和父類同名的方法,則通過子類例項去呼叫該方法時,會呼叫子類中的該方法而不是父類的方法,這個特點我們成為叫做方法的重寫(覆蓋:override)。

故事繼續:徒弟掌握了師父和學院派技術後,自己潛心鑽研出自己的獨門配方的一套全新的煎餅果子技術。

# 1.建立師父類,屬性和方法
class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎餅果子配方]'

    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')


# 2.建立學院派類 屬性和方法
class School(object):
    def __init__(self):
        self.kongfu = '[學院派煎餅果子配方]'

    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')


# 3.獨創配方
class Prentice(School, Master):
    def __init__(self):
        self.kongfu = '[獨創煎餅果子配方]'

    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')


# 4. 用徒弟類建立物件,呼叫例項屬性和方法
tudi = Prentice()
print(tudi.kongfu)
tudi.make_cake()
"""
輸出結果
[獨創煎餅果子配方]
運用[獨創煎餅果子配方]製作煎餅果子
"""

結論:子類和父類具有同名屬性和方法,預設使用子類的同名屬性和方法。

擴充:

當我們呼叫一個物件的方法時,會優先去當前物件中尋找是否具有該方法,如果有則直接呼叫。

如果沒有,則去當前物件的父類中尋找,如果父類中有則直接呼叫父類中的方法。

如果沒有,則去父類的父類中尋找,以此類推,直到找到object,如果依然沒有找到,則報錯。

2、super()函式

在類的繼承中,如果重定義某個方法,該方法會覆蓋父類的同名方法,但有時,我們希望能同時實現父類的功能,這時,我們就需要呼叫父類的方法了,可通過使用 super() 函式來實現。

實際需求就是:子類呼叫父類的同名方法和屬性。

方式一

故事繼續:雖然自己獨創的煎餅果子配方非常受歡迎,但是還是有顧客都希望也能吃到古法和學院技術的煎餅果子。

# 1.建立師父類,屬性和方法
class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎餅果子配方]'

    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')


# 2. 定義徒弟類,繼承師父類
#   新增和父類同名的屬性和方法
class Prentice(Master):
    def __init__(self):
        self.kongfu = '[獨創煎餅果子配方]'

    # 子類的make_cake(self)方法
    def make_cake(self):
        """
        加自己的初始化的原因:
        如果是先呼叫了父類的屬性和方法,父類屬性會覆蓋子類屬性,
        如果不加這個自己的初始化,
        kongfu屬性值是上一次呼叫的init內的kongfu屬性值

        執行方式如下:
        # 建立一個子類物件
        tudi = Prentice()
        # 呼叫執行父類中的make_cake()方法
        tudi.make_master_cake()
        # 呼叫子類中的make_cake()
        tudi.make_cake()
        就能看到劫、結果。

        所以在呼叫屬性前,先呼叫自己子類的初始化,要記住。
        """
        self.__init__()
        print(f'運用{self.kongfu}製作煎餅果子')

    # 呼叫父類的make_cake()方法
    # 子類呼叫父類的同名方法和屬性:把父類的同名屬性和方法再次封裝
    # 呼叫父類方法,但是為保證呼叫到的也是父類的屬性,必須在呼叫方法前呼叫父類的初始化
    def make_master_cake(self):
        # 父類類名.函式()
        # 再次呼叫初始化的原因:這裡想要呼叫父類的同名方法和屬性,
        # 屬性在init初始化位置,所以需要再次呼叫init
        Master.__init__(self)
        Master.make_cake(self)


# 3 . 用徒弟類建立物件,呼叫例項屬性和方法
# 建立一個子類物件
tudi = Prentice()

# 呼叫執行父類中的make_cake()方法
tudi.make_master_cake()

# 呼叫子類中的make_cake()
tudi.make_cake()

"""
輸出結果:
運用[古法煎餅果子配方]製作煎餅果子
運用[獨創煎餅果子配方]製作煎餅果子
"""

方式一:make_master_cake()方法中的程式碼冗餘;父類類名如果變化,那麼在子類中會涉及多處修改,另外,Python是允許多繼承的語言,如上所示的方法在多繼承時就需要重複寫多次,顯得累贅。為了解決這些問題,Python提供了super()函式解決此問題。

方式二

使用super()函式呼叫父類中的同名方法。

# 1.建立師父類,屬性和方法
class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎餅果子配方]'

    def make_cake(self):
        print(f'運用{self.kongfu}製作煎餅果子')


# 2. 定義徒弟類,繼承師父類
#   新增和父類同名的屬性和方法
class Prentice(Master):
    def __init__(self):
        self.kongfu = '[獨創煎餅果子配方]'

    # 子類的make_cake(self)方法
    def make_cake(self):
        self.__init__()
        print(f'運用{self.kongfu}製作煎餅果子')

    # 呼叫父類的make_cake()方法
    # 子類呼叫父類的同名方法和屬性:把父類的同名屬性和方法再次封裝
    # 呼叫父類方法,但是為保證呼叫到的也是父類的屬性,必須在呼叫方法前呼叫父類的初始化
    def make_master_cake(self):
        # 方法二: super()
        # super函式需要傳遞的引數:(當前類名, self)前邊是類名,後邊是物件
        # 方式一: super(當前類名, self).函式()
        # super(Prentice, self).__init__()
        # super(Prentice, self).make_cake()

        # 方式二; 簡便方法,使用super().函式(),引數可省略。
        super().__init__()
        super().make_cake()


# 3 . 用徒弟類建立物件,呼叫例項屬性和方法
# 建立一個子類物件
tudi = Prentice()

# 呼叫執行父類中的make_cake()方法
tudi.make_master_cake()

# 呼叫子類中的make_cake()
tudi.make_cake()

"""
輸出結果:
運用[古法煎餅果子配方]製作煎餅果子
運用[獨創煎餅果子配方]製作煎餅果子
"""

注意:使用super()函式可以自動查詢父類。呼叫順序遵循 __mro__ 類屬性的順序。比較適合單繼承使用。

__mro__內建類屬性說明

在Python中快速檢視一個類的所繼承的父類,以及父類的層級關係的方法,返回的結果是一個元組。

# 如上邊示例
# 語法:類名.__mro__
print(Prentice.__mro__)

"""
輸出結果:
(<class '__main__.Prentice'>, <class '__main__.Master'>, <class 'object'>)

可以看到返回的是一個元組,
`super()`函式在這個元組的類中,依次查詢相關屬性或方法。
"""

相關文章