摘要:本文重點(diǎn)不要試圖在內(nèi)置類型的子類中重寫方法,可以繼承的可拓展類尋求變通掌握多重繼承中的和了解處理多重繼承的一些建議。子類化的代碼如下輸出小結(jié)上述問題只發(fā)生在語言實(shí)現(xiàn)的內(nèi)置類型子類化情況中,而且只影響直接繼承內(nèi)置類型的自定義類。
導(dǎo)語:本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之面向?qū)ο笃闹攸c(diǎn)知識及個(gè)人心得,打算入門Python的朋友們可以來一起學(xué)習(xí)并交流。
本文重點(diǎn):
1、不要試圖在內(nèi)置類型的子類中重寫方法,可以繼承collections的可拓展類尋求變通;一、子類化內(nèi)置類型的缺點(diǎn) 1、內(nèi)置類型的方法不會調(diào)用子類覆蓋的方法
2、掌握多重繼承中的MRO和Super;
3、了解處理多重繼承的一些建議。
內(nèi)置類可以子類化,但是內(nèi)置類型的方法不會調(diào)用子類覆蓋的方法。下面以繼承dict的自定義子類重寫__setitem__為例說明:
class ModifiedDict(dict): def __setitem__(self, key, value): super().__setitem__(key,[value]*2) a=ModifiedDict(one=1) a["two"]=2 print(a) a.update(three=3) print(a)#輸出{"one": 1, "two": [2, 2], "three": 3}
從輸出可以看到,鍵值對one=1和three=3存入a時(shí)均調(diào)用了dict的__setitem__,只有[]運(yùn)算符會調(diào)用我們預(yù)先覆蓋的方法。
問題的解決方式在于不去子類化dict,而是子類化colections.UserDict。
用戶自定義的類應(yīng)該繼承collections模塊,如UserDict,UserList,UserString。這些類做了特殊設(shè)計(jì),因此易于拓展。子類化UserDict的代碼如下:
from collections import UserDict class ModifiedDict(UserDict): def __setitem__(self, key, value): super().__setitem__(key,[value]*2) b=ModifiedDict(one=1) b["two"]=2 b.update(three=3) print(b)#輸出{"one": [1, 1], "two": [2, 2], "three": [3, 3]}
小結(jié):上述問題只發(fā)生在C語言實(shí)現(xiàn)的內(nèi)置類型子類化情況中,而且只影響直接繼承內(nèi)置類型的自定義類。相反,子類化使用Python編寫的類,如UserDict或MutableMapping就不會有此問題。
二、多重繼承 1、方法解析順序(Method Resolution Order,MRO)在多重繼承中存在不相關(guān)的祖先類實(shí)現(xiàn)同名方法引起的沖突問題,這種問題稱作“菱形問題”。Python依靠特定的順序遍歷繼承圖,這個(gè)順序叫做方法解析順序。如圖,左圖是類的UML圖,右圖中的虛線箭頭是方法解析順序。
提到類的屬性__mro__,就會提到super:
super 是個(gè)類,既不是關(guān)鍵字也不是函數(shù)等其他數(shù)據(jù)結(jié)構(gòu)。
作用:super是子類用來調(diào)用父類方法的。
語法:super(a_type, obj);
a_type是obj的__mro__,當(dāng)然也可以是__mro__的一部分,同時(shí)issubclass(obj,a_type)==true
舉個(gè)例子, 有個(gè) MRO: [A, B, C, D, E, object]
我們這樣調(diào)用:super(C, A).foo()
super 只會從 C 之后查找,即: 只會在 D 或 E 或 object 中查找 foo 方法。
下面構(gòu)造一個(gè)菱形問題的多重繼承來深化理解:
class A: def ping(self): print("A-ping:",self) class B(A): def pong(self): print("B-pong:",self) class C(A): def pong(self): print("C-PONG:",self) class D(B, C): def ping(self): print("D-ping:",self) super().ping() def pingpong(self): self.ping() super().ping() self.pong() super(B,D).pong(self) d=D() d.pingpong() print(D.mro())
輸出如下:
D-ping: <__main__.D object at 0x000001B77096EAC8> A-ping: <__main__.D object at 0x000001B77096EAC8>#前兩行對應(yīng)self.ping()。 A-ping: <__main__.D object at 0x000001B77096EAC8>#此處super調(diào)用父類的ping方法。 B-pong: <__main__.D object at 0x000001B77096EAC8> C-PONG: <__main__.D object at 0x000001B77096EAC8>#此處從B之后搜索父類的pong() [, , , , ]#類D的__mro__,數(shù)據(jù)以元組的形式存儲。
分析:d.pingpong()執(zhí)行super.ping(),super按照MRO查找父類的ping方法,查詢在類B到ping之后輸出了B.ping()。
3、處理多重繼承的建議(1)把接口繼承和實(shí)現(xiàn)繼承區(qū)分開;
繼承接口:創(chuàng)建子類型,是框架的支柱;
繼承實(shí)現(xiàn):通過重用避免代碼重復(fù),通??梢該Q用組合和委托模式。
(2)使用抽象基類顯式表示接口;
(3)通過混入重用代碼;
混入類為多個(gè)不相關(guān)的子類提供方法實(shí)現(xiàn),便于重用,但不會實(shí)例化。并且具體類不能只繼承混入類。
(4)在名稱中明確指明混入;
Python中沒有把類聲明為混入的正規(guī)方式,Luciano推薦在名稱中加入Mixin后綴。如Tkinter中的XView應(yīng)變成XViewMixin。
(5)抽象基類可以作為混入,反過來則不成立;
抽象基類與混入的異同:
抽象基類會定義類型,混入做不到;
抽象基類可以作為其他類的唯一基類,混入做不到;
抽象基類實(shí)現(xiàn)的具體方法只能與抽象基類及其超類中的方法協(xié)作,混入沒有這個(gè)局限。
(6)不要子類化多個(gè)具體類;
具體類可以沒有,或者至多一個(gè)具體超類。
例如,Class Dish(China,Japan,Tofu)中,如果Tofu是具體類,那么China和Japan必須是抽象基類或混入。
(7)為用戶提供聚合類;
聚合類是指一個(gè)類的結(jié)構(gòu)主要繼承自混入,自身沒有添加結(jié)構(gòu)或行為。Tkinter采納了此條建議。
(8)優(yōu)先使用對象組合,而不是類繼承。
優(yōu)先使用組合可以令設(shè)計(jì)更靈活。
組合和委托可以代替混入,但不能取代接口繼承去定義類型層次結(jié)構(gòu)。
注:super調(diào)用知識引自
作者: mozillazg
鏈接:https://segmentfault.com/a/11...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/41331.html
摘要:自己定義的抽象基類要繼承。抽象基類可以包含具體方法。這里想表達(dá)的觀點(diǎn)是我們可以偷懶,直接從抽象基類中繼承不是那么理想的具體方法。 抽象基類 抽象基類的常見用途: 實(shí)現(xiàn)接口時(shí)作為超類使用。 然后,說明抽象基類如何檢查具體子類是否符合接口定義,以及如何使用注冊機(jī)制聲明一個(gè)類實(shí)現(xiàn)了某個(gè)接口,而不進(jìn)行子類化操作。 如何讓抽象基類自動識別任何符合接口的類——不進(jìn)行子類化或注冊。 接口在動態(tài)類...
摘要:組合繼承前面兩種模式的特點(diǎn)類式繼承通過子類的原型對父類實(shí)例化實(shí)現(xiàn)的,構(gòu)造函數(shù)式繼承是通過在子類的構(gòu)造函數(shù)作用環(huán)境中執(zhí)行一次父類的構(gòu)造函數(shù)來實(shí)現(xiàn)的。 類有三部分 構(gòu)造函數(shù)內(nèi)的,供實(shí)例化對象復(fù)制用的 構(gòu)造函數(shù)外的,直接通過點(diǎn)語法添加的,供類使用,實(shí)例化對象訪問不到 類的原型中的,實(shí)例化對象可以通過其原型鏈間接地訪問到,也是供所有實(shí)例化對象所共用的。 類式繼承 類的原型對象的作用就是為類...
摘要:借用構(gòu)造函數(shù)繼承針對上面的繼承方法的缺點(diǎn),開發(fā)人員使用一種叫做借用構(gòu)造函數(shù)的技術(shù),也就是我們平時(shí)說的跟繼承。 繼承是 OO 語言中一個(gè)最為津津樂道的概念,許多 OO 語言都支持兩種繼承方式:接口繼承和實(shí)現(xiàn)繼承。接口繼承只繼承方法簽名,而實(shí)現(xiàn)繼承則繼承實(shí)際的方法。由于函數(shù)沒有簽名,在 ECMAScript 中無法實(shí)現(xiàn)接口繼承。ECMAScript 只支持實(shí)現(xiàn)繼承而且實(shí)現(xiàn)繼承主要是依靠原型...
摘要:先來一張圖看看幾個(gè)名詞的關(guān)系構(gòu)造函數(shù)原型實(shí)例原諒我的狂草字體,我手寫比用電腦畫快。今天我們只說原型鏈,所以接下來我就圍繞著原型鏈的幾個(gè)部分說起。每個(gè)函數(shù)都有一個(gè)屬性借用屬性存儲了的原型對象。 先來一張圖看看幾個(gè)名詞的關(guān)系 構(gòu)造函數(shù)、原型、實(shí)例 showImg(https://segmentfault.com/img/remote/1460000018194082); 原諒我的狂草字體,...
閱讀 3791·2021-10-15 09:42
閱讀 2672·2021-09-03 10:50
閱讀 1718·2021-09-03 10:28
閱讀 1836·2019-08-30 15:54
閱讀 2578·2019-08-30 12:46
閱讀 463·2019-08-30 11:06
閱讀 2874·2019-08-30 10:54
閱讀 596·2019-08-29 12:59