摘要:繼承的優(yōu)缺點(diǎn)推出繼承的初衷是讓新手順利使用只有專家才能設(shè)計(jì)出來的框架。多重繼承的真實(shí)應(yīng)用多重繼承能發(fā)揮積極作用。即便是單繼承,這個原則也能提升靈活性,因?yàn)樽宇惢且环N緊耦合,而且較高的繼承樹容易倒。
繼承的優(yōu)缺點(diǎn)
推出繼承的初衷是讓新手順利使用只有專家才能設(shè)計(jì)出來的框架。
——Alan Kay
直接子類化內(nèi)置類型(如 dict、list 或 str)容易出錯,因?yàn)閮?nèi)置類型的 方法通常會忽略用戶覆蓋的方法。
不要子類化內(nèi)置類型,用戶自己定義的類應(yīng)該繼承 collections 模塊的類,
例如UserDict、UserList 和 UserString,這些類做了特殊設(shè)計(jì),因此易于擴(kuò)展。
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"])
綜上,本節(jié)所述的問題只發(fā)生在 C 語言實(shí)現(xiàn)的內(nèi)置類型內(nèi)部的方法委托上,而且只影響 直接繼承內(nèi)置類型的用戶自定義類。
如果子類化使用 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) #看繼承關(guān)系 print(D.__mro__)
直接調(diào)用 d.pong() 運(yùn)行的是 B 類中的版本。
Python 能區(qū)分 d.pong() 調(diào)用的是哪個方法,是因?yàn)?Python 會按照特定的順序遍歷繼承圖。
這個順序叫方法解析順序(Method Resolution Order,MRO)。
類都有一個名為__mro__ 的屬性,它的值是一個元組,按照方法解析順序列出各個超類,從當(dāng)前類一直向上,直到 object 類。D
然而,使用 super() 最安全,也不易過時。調(diào)用框架或不受自己控制的類層次結(jié)構(gòu)中的
方法時,尤其適合使用 super()。
1 多重繼承能發(fā)揮積極作用。
2 《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》一書中的適配器模式用的就是多重繼承,因此使用多重繼承肯定沒有錯
3(那本書中的其他 22 個設(shè)計(jì)模式都使用單繼承,因此多重繼承顯然不是靈丹妙藥)
下面是避免把類圖攪亂的一些建議。
01. 把接口繼承和實(shí)現(xiàn)繼承區(qū)分開使用多重繼承時,一定要明確一開始為什么創(chuàng)建子類。主要原因可能有:
繼承接口,創(chuàng)建子類型,實(shí)現(xiàn)“是什么”關(guān)系
繼承實(shí)現(xiàn),通過重用避免代碼重復(fù)
其實(shí)這兩條經(jīng)常同時出現(xiàn),不過只要可能,一定要明確意圖。通過繼承重用代碼是實(shí)
現(xiàn)細(xì)節(jié),通??梢該Q用組合和委托模式。而接口繼承則是框架的支柱。
現(xiàn)代的 Python 中,如果類的作用是定義接口,應(yīng)該明確把它定義為抽象基類。Python
3.4 及以上的版本中,我們要創(chuàng)建 abc.ABC 或其他抽象基類的子類
python沒有interface這種定義
03. 通過混入重用代碼一個類的作用是為多個不相關(guān)的子類提供方法實(shí)現(xiàn)
應(yīng)該把那個類明確地定義為混入類(mixin class)
從概念上講,混入不定義新類型,只是打包方法,便于重用。
混入類絕對不能實(shí)例化,而且具體類不能只繼承混入類。
混入類應(yīng)該提供某方面的特定行為,只實(shí)現(xiàn)少量關(guān)系非常緊密的方法。
04. 在名稱中明確指明混入因?yàn)樵?Python 中沒有把類聲明為混入的正規(guī)方式,所以強(qiáng)烈推薦在名稱中加入...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 類的定義體是空的,但是這個類提供了有用的服務(wù):
把四個超類結(jié)合在一起,這樣需要創(chuàng)建新小組件的用戶無需記住全部混入,也不用擔(dān)心聲明 class 語句時有沒有遵守特定的順序。08. “優(yōu)先使用對象組合,而不是類繼承”
這句話引自《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》一書, 這是我能提供的最佳
建議。
熟悉繼承之后,就太容易過度使用它了。出于對秩序的訴求,我們喜歡按整潔
的層次結(jié)構(gòu)放置物品,程序員更是樂此不疲。
即便是單繼承,這個原則也能提升靈活性,因?yàn)樽宇惢?br>一種緊耦合,而且較高的繼承樹容易倒。繼承在Django的應(yīng)用
page 417 這里有些復(fù)雜,等我牛掰了再來看
總結(jié)collections.abc 模塊中相應(yīng)的抽象基類
多重繼承這把雙刃劍。首先,我們說明了 mro 類屬性中蘊(yùn)藏的方法解析順序,有了這一機(jī)制,繼承方法的名稱不再會發(fā)生沖突
不要子類化內(nèi)置類型,用戶自己定義的類應(yīng)該繼承 collections 模塊的類
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/44726.html
摘要:例如,的序列協(xié)議只需要和兩個方法。任何類如,只要使用標(biāo)準(zhǔn)的簽名和語義實(shí)現(xiàn)了這兩個方法,就能用在任何期待序列的地方。方法開放了內(nèi)置序列實(shí)現(xiàn)的棘手邏輯,用于優(yōu)雅地處理缺失索引和負(fù)數(shù)索引,以及長度超過目標(biāo)序列的切片。 序列的修改、散列和切片 接著造Vector2d類 要達(dá)到的要求 為了編寫Vector(3, 4) 和 Vector(3, 4, 5) 這樣的代碼,我們可以讓 init 法接受任...
摘要:自己定義的抽象基類要繼承。抽象基類可以包含具體方法。這里想表達(dá)的觀點(diǎn)是我們可以偷懶,直接從抽象基類中繼承不是那么理想的具體方法。 抽象基類 抽象基類的常見用途: 實(shí)現(xiàn)接口時作為超類使用。 然后,說明抽象基類如何檢查具體子類是否符合接口定義,以及如何使用注冊機(jī)制聲明一個類實(shí)現(xiàn)了某個接口,而不進(jìn)行子類化操作。 如何讓抽象基類自動識別任何符合接口的類——不進(jìn)行子類化或注冊。 接口在動態(tài)類...
摘要:但返回的是一個類型的對象,這意味著操作的結(jié)果是一個類型的對象。反之,如果對象存在,這次調(diào)用就會將其作為函數(shù)的輸入,并按照與方法的約定返回一個對象。 一、Optional 類入門 Java 8中引入了一個新的類java.util.Optional。變量存在時,Optional類只是對類簡單封裝。變量不存在時,缺失的值會被建模成一個空的Optional對象,由方法Optional.empt...
摘要:小總結(jié)標(biāo)準(zhǔn)庫里的所有映射類型都是利用來實(shí)現(xiàn)只有可散列的數(shù)據(jù)類型才能用作這些映射里的鍵值不用字典推導(dǎo)用處理找不到的鍵找不到鍵返回某種默認(rèn)值底層是與調(diào)用實(shí)現(xiàn)的字典插入更新原理其他大多數(shù)映射類型都提供了兩個很強(qiáng)大的方法和。 字典和集合 標(biāo)準(zhǔn)庫里的所有映射類型都是利用 dict 來實(shí)現(xiàn)的只有可散列的數(shù)據(jù)類型才能用作這些映射里的鍵(值不用) 可散列 一個對象是可散列的 它的散列值是不變的 對象...
摘要:第一章數(shù)據(jù)類型隱式方法利用快速生成類方法方法通過下標(biāo)找元素自動支持切片操作可迭代方法與如果是一個自定義類的對象,那么會自己去調(diào)用其中由你實(shí)現(xiàn)的方法。若返回,則會返回否則返回。一個對象沒有函數(shù),解釋器會用作為替代。 第一章 python數(shù)據(jù)類型 1 隱式方法 利用collections.namedtuple 快速生成類 import collections Card = collec...
閱讀 2616·2021-11-18 10:02
閱讀 2642·2021-11-15 11:38
閱讀 3723·2021-11-12 10:36
閱讀 712·2021-11-12 10:34
閱讀 2920·2021-10-21 09:38
閱讀 1506·2021-09-29 09:48
閱讀 1512·2021-09-29 09:34
閱讀 1104·2021-09-22 10:02