
繼承和多型
繼承和多型——這是Python中一個非常重要的概念。如果你想學習,必須更好地理解它。
繼承
面向物件程式設計的主要優勢之一是程式碼重用。繼承是實現此目標的機制之一。繼承允許程式設計師首先建立一個通用或基礎類,然後將其擴充套件到更專業的類。它允許程式設計師編寫更好的程式碼。
使用繼承,您可以使用或繼承基礎類中可用的所有資料欄位和方法。稍後您可以新增自己的方法和資料欄位,因此繼承提供了一種組織程式碼的方式,而不是從頭開始重寫。
在面向物件的術語中,當類X擴充套件類Y時,Y稱為超類/父類/基類,X稱為子類/子類/派生類。這裡需要注意的一點是,只有非私有的資料欄位和方法才能被子類訪問。私有資料欄位和方法只能在類內部訪問。
建立派生類的語法為:
class BaseClass: Body of base class class DerivedClass(BaseClass): Body of derived class
繼承屬性
現在看看下面的例子:

輸出

我們首先建立了一個名為Date的類,並將物件作為引數傳遞,這裡物件是Python提供的內建類。稍後我們建立了另一個名為time的類,並將Date類作為引數呼叫。透過此呼叫,我們可以訪問Time類中Date類中的所有資料和屬性。正因為如此,當我們嘗試從之前建立的Time類物件tm獲取get_date方法時,這是可能的。
物件.屬性查詢層次結構
- 例項
- 類
- 任何此類繼承的類
繼承示例
讓我們仔細看看繼承示例:

讓我們建立幾個類來參與示例:
- Animal - 模擬動物的類
- Cat - Animal的子類
- Dog - Animal的子類
在Python中,類的建構函式用於建立物件(例項),併為屬性賦值。
子類的建構函式始終呼叫父類的建構函式來初始化父類中屬性的值,然後開始為其自己的屬性賦值。

輸出

在上面的示例中,我們看到了在父類中放置的命令屬性或方法,以便所有子類或子類將繼承父類的該屬性。
如果子類試圖從另一個子類繼承方法或資料,則會丟擲錯誤,就像我們在Dog類嘗試從Cat類呼叫swatstring()方法時看到的那樣,它會丟擲錯誤(在我們的例子中是AttributeError)。
多型(“多種形態”)
多型是Python中類定義的一個重要特性,當您在類或子類中具有同名方法時會使用它。這允許函式在不同時間使用不同型別的實體。因此,它提供了靈活性和松耦合,以便程式碼可以隨著時間的推移進行擴充套件和輕鬆維護。
這允許函式使用任何這些多型類的物件,而無需瞭解類之間的區別。
多型可以透過繼承來實現,子類利用基類方法或覆蓋它們。
讓我們用之前的繼承示例來理解多型的概念,並在兩個子類中新增一個名為show_affection的通用方法:
從示例中我們可以看到,它指的是一種設計,其中不同型別的物件可以以相同的方式對待,或者更具體地說,兩個或多個類具有相同名稱的方法或通用介面,因為相同的方法(下面的示例中的show_affection)被用於任何型別的物件。

輸出

因此,所有動物都表現出感情(show_affection),但它們的方式不同。因此,“show_affection”行為在某種意義上是多型的,因為它根據動物的不同而表現不同。因此,抽象的“動物”概念實際上並不“表現出感情”,但特定的動物(如狗和貓)對“表現出感情”的行為有具體的實現。
Python本身包含多型的類。例如,len()函式可以用於多個物件,並且所有函式都根據輸入引數返回正確的輸出。

覆蓋
在Python中,當子類包含覆蓋超類方法的方法時,您還可以透過呼叫以下方法來呼叫超類方法
Super(Subclass, self).method 而不是 self.method。
示例
class Thought(object): def __init__(self): pass def message(self): print("Thought, always come and go") class Advice(Thought): def __init__(self): super(Advice, self).__init__() def message(self): print('Warning: Risk is always involved when you are dealing with market!')
繼承建構函式
如果我們從之前的繼承示例中看到,__init__位於父類中,因為子類dog或cat在其內部沒有__init__方法。Python使用繼承屬性查詢在animal類中查詢__init__。當我們建立子類時,首先它將在dog類中查詢__init__方法,然後它沒有找到它,然後在父類Animal中查詢並找到它並在那裡呼叫它。因此,隨著我們類設計的複雜化,我們可能希望首先透過父類建構函式處理例項,然後透過子類建構函式處理例項。

輸出

在上面的示例中,所有動物都有一個名字,所有狗都有一個特定的品種。我們使用super呼叫了父類建構函式。因此,dog有自己的__init__,但首先發生的事情是我們呼叫super。Super是內建函式,它旨在將類與其超類或父類關聯。
在這種情況下,我們說獲取dog的超類並將dog例項傳遞給我們在這裡說的任何方法,即建構函式__init__。換句話說,我們正在使用dog物件呼叫父類Animal __init__。您可能會問為什麼我們不只是用dog例項說Animal __init__,我們可以這樣做,但如果animal類的名稱將來發生變化。如果我們想重新排列類層次結構,那麼dog從另一個類繼承。在這種情況下使用super允許我們保持事物模組化並易於更改和維護。
因此,在這個例子中,我們能夠將通用__init__功能與更具體的**功能結合起來。這給了我們機會將通用功能與特定功能分開,這可以消除程式碼重複,並以反映系統整體設計的方式將類相互關聯。
結論
__init__就像任何其他方法一樣;它可以被繼承
如果一個類沒有__init__建構函式,Python將檢查其父類以檢視是否可以找到一個。
一旦找到一個,Python就會呼叫它並停止查詢
我們可以使用super()函式呼叫父類中的方法。
我們可能希望在父類和我們自己的類中進行初始化。
多重繼承和查詢樹
顧名思義,Python中的多重繼承是指一個類繼承自多個類。
例如,一個孩子繼承父母雙方的性格特徵(母親和父親)。
Python多重繼承語法
要使一個類繼承自多個父類,我們在定義派生類時將這些類的名稱寫在派生類的括號內。我們用逗號分隔這些名稱。
下面是一個例子:
>>> class Mother: pass >>> class Father: pass >>> class Child(Mother, Father): pass >>> issubclass(Child, Mother) and issubclass(Child, Father) True
多重繼承指的是從兩個或多個類繼承的能力。複雜性在於子類從父類繼承,而父類又從祖父母類繼承。Python會沿著繼承樹向上查詢被請求從物件讀取的屬性。它將在例項、類中檢查,然後是父類,最後是祖父母類。現在問題出現了,類將以什麼順序被搜尋——廣度優先還是深度優先。預設情況下,Python使用深度優先。
這就是為什麼在下面的圖表中,Python首先在類A中搜索dothis()方法。因此,在下面的示例中,方法解析順序將為
Mro- D→B→A→C
檢視下面的多重繼承圖:

讓我們透過一個例子來理解Python的“mro”特性。
輸出

示例3
讓我們再舉一個“菱形”多重繼承的例子。

上面的圖表將被認為是模稜兩可的。從我們之前理解“方法解析順序”的例子中。即mro將是D→B→A→C→A,但事實並非如此。在從C獲取第二個A時,Python會忽略之前的A。因此,在這種情況下,mro將為D→B→C→A。
讓我們根據上面的圖表建立一個例子:

輸出

理解上面輸出的簡單規則是——如果同一個類出現在方法解析順序中,則該類的早期出現將從方法解析順序中刪除。
總之:
任何類都可以繼承自多個類
Python通常在搜尋繼承類時使用“深度優先”順序。
但是當兩個類繼承自同一個類時,Python會從mro中刪除該類的第一次出現。
裝飾器、靜態方法和類方法
函式(或方法)由def語句建立。
儘管方法的工作方式與函式完全相同,但有一點不同,即方法的第一個引數是例項物件。
我們可以根據方法的行為對其進行分類,例如
簡單方法 - 在類外部定義。此函式可以透過提供例項引數來訪問類屬性
def outside_func(():
例項方法 -
def func(self,)
類方法 - 如果我們需要使用類屬性
@classmethod def cfunc(cls,)
靜態方法 - 沒有關於類的任何資訊
@staticmethod def sfoo()
到目前為止,我們已經看到了例項方法,現在是時候深入瞭解其他兩種方法了,
類方法
@classmethod 裝飾器是一個內建的函式裝飾器,它將呼叫它的類或呼叫它的例項的類作為第一個引數傳遞。該評估的結果會覆蓋你的函式定義。
語法
class C(object): @classmethod def fun(cls, arg1, arg2, ...): .... fun: function that needs to be converted into a class method returns: a class method for function
它們可以訪問這個 cls 引數,但不能修改物件例項的狀態。這需要訪問 self。
它繫結到類,而不是類的物件。
類方法仍然可以修改適用於類所有例項的類狀態。
靜態方法
靜態方法既不接受 self 引數也不接受 cls(類)引數,但可以自由接受任意數量的其他引數。
語法
class C(object): @staticmethod def fun(arg1, arg2, ...): ... returns: a static method for function funself.
- 靜態方法既不能修改物件狀態也不能修改類狀態。
- 它們在可以訪問的資料方面受到限制。
何時使用什麼
我們通常使用類方法來建立工廠方法。工廠方法為不同的用例返回類物件(類似於建構函式)。
我們通常使用靜態方法來建立實用程式函式。