摘要:繼承的優缺點推出繼承的初衷是讓新手順利使用只有專家才能設計出來的框架。多重繼承的真實應用多重繼承能發揮積極作用。即便是單繼承,這個原則也能提升靈活性,因為子類化是一種緊耦合,而且較高的繼承樹容易倒。
繼承的優缺點
推出繼承的初衷是讓新手順利使用只有專家才能設計出來的框架。
——Alan Kay
直接子類化內置類型(如 dict、list 或 str)容易出錯,因為內置類型的 方法通常會忽略用戶覆蓋的方法。
不要子類化內置類型,用戶自己定義的類應該繼承 collections 模塊的類,
例如UserDict、UserList 和 UserString,這些類做了特殊設計,因此易于擴展。
import collections class DoppelDict2(collections.UserDict): def __setitem__(self, key, value): super().__setitem__(key, [value] * 2) dd = DoppelDict2(one=1) print(dd) dd["two"] = 2 print(dd) dd.update(three=3) print(dd) class AnswerDict2(collections.UserDict): def __getitem__(self, key): return 42 ad = AnswerDict2(a="foo") print(ad["a"])
綜上,本節所述的問題只發生在 C 語言實現的內置類型內部的方法委托上,而且只影響 直接繼承內置類型的用戶自定義類。
如果子類化使用 Python 編寫的類,如 UserDict 或 MutableMapping,就不會受此影響。
多重繼承和方法解析順序class A: def ping(self): print("ping:", self) class B(A): def pong(self): print("pong:", self) class C(A): def pong(self): print("PONG:", self) class D(B, C): def ping(self): super().ping() print("post-ping:", self) def pingpong(self): self.ping() super().ping() self.pong() super().pong() C.pong(self) d = D() d.pong() C.pong(d) #看繼承關系 print(D.__mro__)
直接調用 d.pong() 運行的是 B 類中的版本。
Python 能區分 d.pong() 調用的是哪個方法,是因為 Python 會按照特定的順序遍歷繼承圖。
這個順序叫方法解析順序(Method Resolution Order,MRO)。
類都有一個名為__mro__ 的屬性,它的值是一個元組,按照方法解析順序列出各個超類,從當前類一直向上,直到 object 類。D
然而,使用 super() 最安全,也不易過時。調用框架或不受自己控制的類層次結構中的
方法時,尤其適合使用 super()。
1 多重繼承能發揮積極作用。
2 《設計模式:可復用面向對象軟件的基礎》一書中的適配器模式用的就是多重繼承,因此使用多重繼承肯定沒有錯
3(那本書中的其他 22 個設計模式都使用單繼承,因此多重繼承顯然不是靈丹妙藥)
下面是避免把類圖攪亂的一些建議。
01. 把接口繼承和實現繼承區分開使用多重繼承時,一定要明確一開始為什么創建子類。主要原因可能有:
繼承接口,創建子類型,實現“是什么”關系
繼承實現,通過重用避免代碼重復
其實這兩條經常同時出現,不過只要可能,一定要明確意圖。通過繼承重用代碼是實
現細節,通常可以換用組合和委托模式。而接口繼承則是框架的支柱。
現代的 Python 中,如果類的作用是定義接口,應該明確把它定義為抽象基類。Python
3.4 及以上的版本中,我們要創建 abc.ABC 或其他抽象基類的子類
python沒有interface這種定義
03. 通過混入重用代碼一個類的作用是為多個不相關的子類提供方法實現
應該把那個類明確地定義為混入類(mixin class)
從概念上講,混入不定義新類型,只是打包方法,便于重用。
混入類絕對不能實例化,而且具體類不能只繼承混入類。
混入類應該提供某方面的特定行為,只實現少量關系非常緊密的方法。
04. 在名稱中明確指明混入因為在 Python 中沒有把類聲明為混入的正規方式,所以強烈推薦在名稱中加入...Mixin 后綴。
Tkinter 沒有采納這個建議,如果采納的話,XView 會變成XViewMixin,Pack 會變成 PackMixin
05. 為用戶提供聚合類class Widget(BaseWidget, Pack, Place, Grid): """Internal class. Base class for a widget which can be positioned with the geometry managers Pack, Place or Grid.""" pass
Widget 類的定義體是空的,但是這個類提供了有用的服務:
把四個超類結合在一起,這樣需要創建新小組件的用戶無需記住全部混入,也不用擔心聲明 class 語句時有沒有遵守特定的順序。08. “優先使用對象組合,而不是類繼承”
這句話引自《設計模式:可復用面向對象軟件的基礎》一書, 這是我能提供的最佳
建議。
熟悉繼承之后,就太容易過度使用它了。出于對秩序的訴求,我們喜歡按整潔
的層次結構放置物品,程序員更是樂此不疲。
即便是單繼承,這個原則也能提升靈活性,因為子類化是繼承在Django的應用
一種緊耦合,而且較高的繼承樹容易倒。
page 417 這里有些復雜,等我牛掰了再來看
總結collections.abc 模塊中相應的抽象基類
多重繼承這把雙刃劍。首先,我們說明了 mro 類屬性中蘊藏的方法解析順序,有了這一機制,繼承方法的名稱不再會發生沖突
不要子類化內置類型,用戶自己定義的類應該繼承 collections 模塊的類
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/44726.html
摘要:例如,的序列協議只需要和兩個方法。任何類如,只要使用標準的簽名和語義實現了這兩個方法,就能用在任何期待序列的地方。方法開放了內置序列實現的棘手邏輯,用于優雅地處理缺失索引和負數索引,以及長度超過目標序列的切片。 序列的修改、散列和切片 接著造Vector2d類 要達到的要求 為了編寫Vector(3, 4) 和 Vector(3, 4, 5) 這樣的代碼,我們可以讓 init 法接受任...
摘要:自己定義的抽象基類要繼承。抽象基類可以包含具體方法。這里想表達的觀點是我們可以偷懶,直接從抽象基類中繼承不是那么理想的具體方法。 抽象基類 抽象基類的常見用途: 實現接口時作為超類使用。 然后,說明抽象基類如何檢查具體子類是否符合接口定義,以及如何使用注冊機制聲明一個類實現了某個接口,而不進行子類化操作。 如何讓抽象基類自動識別任何符合接口的類——不進行子類化或注冊。 接口在動態類...
摘要:但返回的是一個類型的對象,這意味著操作的結果是一個類型的對象。反之,如果對象存在,這次調用就會將其作為函數的輸入,并按照與方法的約定返回一個對象。 一、Optional 類入門 Java 8中引入了一個新的類java.util.Optional。變量存在時,Optional類只是對類簡單封裝。變量不存在時,缺失的值會被建模成一個空的Optional對象,由方法Optional.empt...
摘要:小總結標準庫里的所有映射類型都是利用來實現只有可散列的數據類型才能用作這些映射里的鍵值不用字典推導用處理找不到的鍵找不到鍵返回某種默認值底層是與調用實現的字典插入更新原理其他大多數映射類型都提供了兩個很強大的方法和。 字典和集合 標準庫里的所有映射類型都是利用 dict 來實現的只有可散列的數據類型才能用作這些映射里的鍵(值不用) 可散列 一個對象是可散列的 它的散列值是不變的 對象...
摘要:第一章數據類型隱式方法利用快速生成類方法方法通過下標找元素自動支持切片操作可迭代方法與如果是一個自定義類的對象,那么會自己去調用其中由你實現的方法。若返回,則會返回否則返回。一個對象沒有函數,解釋器會用作為替代。 第一章 python數據類型 1 隱式方法 利用collections.namedtuple 快速生成類 import collections Card = collec...
閱讀 2591·2021-11-18 10:02
閱讀 2627·2021-11-15 11:38
閱讀 3699·2021-11-12 10:36
閱讀 696·2021-11-12 10:34
閱讀 2888·2021-10-21 09:38
閱讀 1479·2021-09-29 09:48
閱讀 1492·2021-09-29 09:34
閱讀 1088·2021-09-22 10:02