成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Python學(xué)習(xí)之路31-繼承的利弊

tinylcy / 571人閱讀

摘要:使用抽象基類顯示表示接口如果類的作用是定義接口,應(yīng)該將其明確定義為抽象基類。此外,抽象基類可以作為其他類的唯一基類,混入類則決不能作為唯一的基類,除非這個混入類繼承了另一個更具體的混入這種做法非常少見。

《流暢的Python》筆記

本篇是“面向?qū)ο髴T用方法”的第五篇,我們將繼續(xù)討論繼承,重點(diǎn)說明兩個方面:繼承內(nèi)置類型時的問題以及多重繼承。概念比較多,較為枯燥。

1. 繼承內(nèi)置類型

內(nèi)置類型(C語言編寫)的方法通常會忽略用戶重寫的方法,這種行為體現(xiàn)在兩方面:

內(nèi)置類型A的子類ChildA即使重寫了A中的方法,當(dāng)ChildA調(diào)用這些方法時,也不一定調(diào)用的就是重寫的版本,而依然可能調(diào)用A中的版本;

內(nèi)置類型B調(diào)用ChildA的方法時,調(diào)用的也不一定是被ChildA重寫的方法,可能依然會調(diào)用A的版本。

dict__getitem__方法為例,即使這個方法被子類重寫了,內(nèi)置類型的get()方法也不一定調(diào)用重寫的版本:

# 代碼1.1
>>> class MyDict(dict):
...     def __getitem__(self, key):
...         return "Test"   # 不管要獲取誰,都返回"Test"
...    
>>> child = MyDict({"one":1, "two":2})
>>> child
{"one": 1, "two": 2}    # 正常
>>> child["one"]
"Test"    # 此時也是正常的
>>> child.get("one")
1   # 這里就不正常了,按理說應(yīng)該返回"Test"
>>> b = {}
>>> b.update(child)
>>> b  # 并沒有調(diào)用child的__getitem__方法
{"one": 1, "two": 2}

這是在CPython中的情況,這些行為其實(shí)違背了面向?qū)ο缶幊痰囊粋€基本原則,即應(yīng)該始終從實(shí)例所屬的類開始搜索方法,即使在超類實(shí)現(xiàn)的類中調(diào)用也應(yīng)該如此。但實(shí)際是可能直接調(diào)用基類的方法,而不先搜索子類。這種設(shè)定并不能說是錯誤的,這只是一種取舍,畢竟這也是CPython中的內(nèi)置類型運(yùn)行得快的原因之一,但這種方式就給我們出了難題。這種問題的解決方法有兩個:

重寫從內(nèi)置類型繼承來的所有方法(要真這樣,那我還繼承干啥?),或者查看源碼,把相關(guān)的方法都給重寫了(誰的記性能這么好?);

第二種方法才是推薦的方法:如果要繼承內(nèi)置類型,請從collections模塊中繼承,比如繼承自UserListUserDictUserString。這些類不是用C語言寫的,而是用純Python寫的,并且嚴(yán)格遵循了上述面向?qū)ο蟮脑瓌t。如果上述代碼中的MyDict繼承自UserDict,行為則會合乎預(yù)期。

強(qiáng)調(diào):本節(jié)所述問題只發(fā)生在C語言實(shí)現(xiàn)的內(nèi)置類型內(nèi)部的方法委托上,而且只影響直接繼承內(nèi)置類型的自定義類。如果子類繼承自純Python編寫的類,則不會有此問題。

2.多重繼承

任何實(shí)現(xiàn)多重繼承的語言都要處理潛在的命名沖突,這種沖突由不相關(guān)的超類實(shí)現(xiàn)同名方法引起。這種沖突稱為”菱形沖突“。

2.1 多重繼承的示例

下面是我們要實(shí)現(xiàn)的類的UML圖:

紅線表示超類的調(diào)用順序,以下是它的實(shí)現(xiàn):

# 代碼2.1
class A:
    def ping(self):
        print("ping in A:", self)

class B(A):
    def pong(self):
        print("pong in B:", self)

class C(A):
    def pong(self):
        print("PONG in C:", self)

class D(B, C):
    def ping(self):
        super().ping()
        print("ping in D:", self)

    def pingpong(self):
        self.ping()
        super().ping()
        self.pong()
        super().pong()
        C.pong(self)   # 在定義時調(diào)用特定父類的寫法,顯示傳入self參數(shù)

# 下面是它在控制臺中的調(diào)用情況
>>> from diamond import *
>>> d = D()
>>> d.pong()
pong in B: 
>>> d.pingpong()
ping in A:    # self.ping()
ping in D: 
ping in A:    # super().ping()
pong in B:    # self.pong()
pong in B:    # super().pong()
PONG in C:    # C.pong(self)
>>> C.pong(d)    # 在運(yùn)行時調(diào)用特定父類的寫法,顯示傳入實(shí)例參數(shù)
PONG in C: 
>>> D.__mro__   # Method Resolutino Order,方法解析順序,上一篇文章中有所提及
(, , , 
 , )

類都有一個名為__mro__ 的屬性,它的值是一個元組,按一定順序列舉超類,這個順序由C3算法計算。

方法解析順序不僅考慮繼承圖,還考慮子類聲明中列出超類的順序。例如,如果D類的聲明改為class D(C, B),那么D則會先搜索C,再搜索B

若想把方法調(diào)用委托給超類,推薦的做法是使用內(nèi)置的super()函數(shù);同時,還請注意上述調(diào)用特定超類的語法。然而,使用super()是最安全的,也不易過時。調(diào)用框架或不受自己控制的類層次結(jié)構(gòu)中的方法時,尤其應(yīng)該使用super()。

2.2 處理多重繼承的建議

繼承有很多用途,而多重繼承增加了可選方案和復(fù)雜度。使用多重繼承容易得出令人費(fèi)解和脆弱的設(shè)計。以下是8條避免產(chǎn)生混亂類圖的建議:

把接口繼承和實(shí)現(xiàn)繼承區(qū)分開

在使用多重繼承時,一定要明白自己為什么要創(chuàng)建子類:

繼承接口,創(chuàng)建子類,實(shí)現(xiàn)“是什么(”is-a”)”關(guān)系;

繼承實(shí)現(xiàn),通過重用避免代碼重復(fù)

其實(shí)這倆經(jīng)常同時出現(xiàn),不過只要有可能,一定要明確這么做的意圖。通過繼承重用代碼是實(shí)現(xiàn)細(xì)節(jié),通??梢該Q成用組合和委托的模式,而接口繼承則是框架的支柱。

使用抽象基類顯示表示接口

如果類的作用是定義接口,應(yīng)該將其明確定義為抽象基類。

通過“混入類”實(shí)現(xiàn)代碼重用

如果一個類的作用是為多個不相關(guān)的子類提供方法實(shí)現(xiàn),從而實(shí)現(xiàn)重用,但不體現(xiàn)“is-a”關(guān)系,則應(yīng)該把那個類明確定義為混入類(mixin class)。從概念上講,混入不定義新類型,只是打包方法,便于重用。混入類絕對不能實(shí)例化,而且具體類不能只繼承混入類。混入類應(yīng)該提供某方面的特定行為,只實(shí)現(xiàn)少量關(guān)系非常緊密的方法。

在名稱中明確指明混入

由于Python沒有把類明確聲明為混入的正式方式,實(shí)際的做法是在類名后面加入Mixin后綴。Python的GUI庫Tkinter沒有采用這種方法,這也是它的類圖十分混亂的原因之一,而Django則采用了這種方式。

抽象基類可以作為混入類,但混入類不能作為抽象基類

抽象基類可以實(shí)現(xiàn)具體方法,因此可以作為混入類使用。但抽象基類能定義數(shù)據(jù)類型,混入類則做不到。此外,抽象基類可以作為其他類的唯一基類,混入類則決不能作為唯一的基類,除非這個混入類繼承了另一個更具體的混入(這種做法非常少見)。

但值得注意的是,抽象基類中的具體方法只是一種便利措施,因?yàn)樗荒苷{(diào)用抽象基類及其超類中定義了的方法,那么用戶自行調(diào)用這些方法也可以實(shí)現(xiàn)同樣的功能,所以,抽象基類也并不常作為混入類。

不要從多個具體類繼承

應(yīng)該盡量保證具體類沒有或者最多只有一個具體超類。也就是說,具體類的超類中除了這一個具體超類外,其余的都應(yīng)該是抽象基類或混入類。

為用戶提供聚合類

如果抽象基類或混入類的組合對客戶代碼非常有用,那就提供一個類,使用易于理解的方式把它們結(jié)合起來,這種類被稱為聚合類。比如tkinter.Widget類,它的定義如下:

# 代碼2.2
class Widget(BaseWidget, Pack, Place, Grid):  # 省略掉了文檔注釋
    pass

它的定義體是空的,但通過這一個類,提供了四個超類的全部方法。

優(yōu)先使用對象組合,而不是類繼承

優(yōu)先使用組合能讓設(shè)計更靈活。即便是單繼承,這個原則也能提升靈活性,因?yàn)槔^承是一種緊耦合,而且較高的繼承樹容易倒。組合和委托還可以代替混入類,把行為提供給不同的類,但它不能取代接口繼承,因?yàn)榻涌诶^承定義的是類層次結(jié)構(gòu)。

迎大家關(guān)注我的微信公眾號"代碼港" & 個人網(wǎng)站 www.vpointer.net ~

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/41945.html

相關(guān)文章

  • Python學(xué)習(xí)之路8.2-對Python補(bǔ)充

    摘要:本章主要是對上一章類的補(bǔ)充。對于多態(tài)的補(bǔ)充子類可以被看成是父類的類型,但父類不能被看成是子類的類型。仍然以類為例,動物里有哺乳動物,卵生動物,有能飛的動物和不能飛的動物,這是兩種大的分類方式。一般在中,以為結(jié)尾類的都作為接口。 《Python編程:從入門到實(shí)踐》筆記。本章主要是對上一章Python類的補(bǔ)充。 1. 從一個類派生出所有類 上一篇文章說道Python類的定義與繼承一般是如下...

    liukai90 評論0 收藏0
  • Vue:scoped與module使用與利弊

    摘要:一個應(yīng)用是離不開與,其中充斥的整個項目中。下面我會分別對與解決方案進(jìn)行說明,最后在分析它們的利弊與選擇。不過一個子組件的根節(jié)點(diǎn)會同時受其父組件有作用域的和子組件有作用域的的影響。但它的局限性是適用于中小項目中。 showImg(https://segmentfault.com/img/bVbnIPd?w=900&h=383); 一個web應(yīng)用是離不開html、css與js,其中css充...

    zhoutk 評論0 收藏0
  • Vue:scoped與module使用與利弊

    摘要:一個應(yīng)用是離不開與,其中充斥的整個項目中。下面我會分別對與解決方案進(jìn)行說明,最后在分析它們的利弊與選擇。不過一個子組件的根節(jié)點(diǎn)會同時受其父組件有作用域的和子組件有作用域的的影響。但它的局限性是適用于中小項目中。 showImg(https://segmentfault.com/img/bVbnIPd?w=900&h=383); 一個web應(yīng)用是離不開html、css與js,其中css充...

    zr_hebo 評論0 收藏0
  • Vue:scoped與module使用與利弊

    摘要:一個應(yīng)用是離不開與,其中充斥的整個項目中。下面我會分別對與解決方案進(jìn)行說明,最后在分析它們的利弊與選擇。不過一個子組件的根節(jié)點(diǎn)會同時受其父組件有作用域的和子組件有作用域的的影響。但它的局限性是適用于中小項目中。 showImg(https://segmentfault.com/img/bVbnIPd?w=900&h=383); 一個web應(yīng)用是離不開html、css與js,其中css充...

    missonce 評論0 收藏0
  • Python學(xué)習(xí)之路30-接口:從協(xié)議到抽象基類

    摘要:本篇內(nèi)容將從鴨子類型的動態(tài)協(xié)議,逐漸過渡到使接口更明確能驗(yàn)證實(shí)現(xiàn)是否符合規(guī)定的抽象基類。抽象基類介紹完動態(tài)實(shí)現(xiàn)接口后,現(xiàn)在開始討論抽象基類,它屬于靜態(tài)顯示地實(shí)現(xiàn)接口。標(biāo)準(zhǔn)庫中的抽象基類從開始,標(biāo)準(zhǔn)庫提供了抽象基類。 《流暢的Python》筆記。本篇是面向?qū)ο髴T用方法的第四篇,主要討論接口。本篇內(nèi)容將從鴨子類型的動態(tài)協(xié)議,逐漸過渡到使接口更明確、能驗(yàn)證實(shí)現(xiàn)是否符合規(guī)定的抽象基類(Abst...

    LucasTwilight 評論0 收藏0

發(fā)表評論

0條評論

tinylcy

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<