流暢的Python 繼承的優缺點

2021-10-02 16:07:20 字數 4487 閱讀 6273

1. 子類化內建型別很麻煩

在python3中,內建型別可以子類化,但是有個重要的注意事項:內建型別(cpython)不會呼叫使用者定義的類覆蓋的特殊方法。

內建型別的方法不會呼叫子類覆蓋的方法。例如,dict 的子類覆蓋的__getitem__()方法不會被內建型別的get()方法呼叫。

class

doppeldict

(dict):

def__setitem__

(self, key, value)

:super()

.__setitem__(key,

[key]*2

)d = doppeldict(one=1)

d["two"]=

2d.update(three=3)

print

(d)#

可以看出繼承自 dict 的__init__update方法顯然忽略了覆蓋的__setitem__方法, 運算子會呼叫覆蓋的__setitem__方法。

原生型別的這種行為違背了物件導向程式設計的乙個基本原則:始終應該從例項(self)所屬的類開始搜尋方法,即使在超類實現的類中呼叫也是如此。在這種糟糕的局面中,__missing__方法卻能按預期方式工作,不過這只是特例。

不只例項內部的呼叫有這個問題(self.get()不呼叫self.__getitem__()),內建型別的方法呼叫的其他類的方法,如果被覆蓋了,也不會被呼叫。

class

answerdict

(dict):

def__getitem__

(self, item)

:return

100ad = answerdict(one=1)

print

(ad[

"one"])

# 100 不管傳入什麼鍵,answerdict.__getitem__ 方法始終返回100。

new_ad =

new_ad.update(ad)

print

(new_ad)

# print

(new_ad[

"one"])

# 1 dict.update 方法忽略了answerdict.__getitem__方法。

小結:直接子類化內建型別(如 dict、list 或 str)容易出錯,因為內建型別的方法通常會忽略使用者覆蓋的方法。不要子類化內建型別,使用者自己定義的類應該繼承 collections 模組中的類,例如 userdict、userlist 和 userstring,這些類做了特殊設計,因此易於擴充套件。

from collections import userdict

class

doppeldict

(userdict)

:def

__setitem__

(self, key, value)

:super()

.__setitem__(key,

[key]*2

)class

answerdict

(userdict)

:def

__getitem__

(self, item)

:return

100

2. 多重繼承和方法解析順序

任何實現多重繼承的語言都要處理潛在的命名衝突,這種衝突由不相關的祖先類實現同名方法引起。

)# 直接呼叫 d.pong() 執行的是 b 類中的版本

d.pong(

)# b pong <__main__.d object at>

# 超類中的方法都可以直接呼叫,此時要把例項作為顯式引數傳入

c.pong(d)

# c pong <__main__.d object at>

b.pong(d)

# b pong <__main__.d object at>

python 能區分 d.pong() 呼叫的是哪個方法,是因為 python 會按照特定的順序遍歷繼承圖。這個順序叫方法解析順序(method resolution order,mro)。類都有乙個名為__mro__的屬性,它的值是乙個元組,按照方法解析順序列出各個超類,從當前類一直向上,直到object 類。有了這一機制,繼承方法的名稱不再會發生衝突。

print

(d.__mro__)

# (, , , , )

print

(bool

.__mro__)

# 可以看出 bool 從 int 和 object 中繼承方法和屬性。

# (, , )

若想把方法呼叫委託給超類,推薦的方式是使用內建的 super() 函式。然而,有時可能需要繞過方法解析順序,直接呼叫某個超類的方法——這樣做有時更方便。例如,d.ping 方法可以這樣寫:

def

ping

(self)

:# super().ping()

a.ping(self)

# 直接在類上呼叫例項方法時,必須顯式傳入self引數,因為這樣訪問的是未繫結方法(unbound method)

print

("d ping"

, self)

使用 super() 最安全,也不易過時。呼叫框架或不受自己控制的類層次結構中的方法時,尤其適合使用 super()。使用 super() 呼叫方法時,會遵守方法解析順序。

d.ping(

)# a ping <__main__.d object at>

# d ping <__main__.d object at>

def

pingpong

(self)

: self.ping(

)# a ping、d ping、

super()

.ping(

)# a ping

self.pong(

)# b pong

super()

.pong(

)# b pong

c.pong(self)

# c pong

其中,第三個呼叫是 self.pong(),根據__mro__,找到的是 b 類實現的 pong 方法。第四個呼叫是 super().pong(),也根據__mro__,找到 b 類實現的 pong 方法。第五個呼叫是 c.pong(self),忽略__mro__,找到的是 c 類實現的 pong 方法。

方法解析順序不僅考慮繼承圖,還考慮子類宣告中列出超類的順序。也就是說,如果在把 d 類宣告為class d(c, b):,那麼 d 類的__mro__屬性就會不一樣:先搜尋 c 類,再搜尋 b 類。

本章還有後續,暫不納入了。

Python中繼承的優缺點

1 不要試圖在內建型別的子類中重寫方法,可以繼承collections的可拓展類尋求變通 2 掌握多重繼承中的mro和super 3 了解處理多重繼承的一些建議。1 內建型別的方法不會呼叫子類覆蓋的方法 內建類可以子類化,但是內建型別的方法不會呼叫子類覆蓋的方法。下面以繼承dict的自定義子類重寫 ...

組合 繼承的優缺點

組合的優點和缺點 n 優點 f 容器類僅能通過被包含物件的介面來對其進行訪問。f 黑盒 復用,因為被包含物件的內部細節對外是不可見。f 對裝性好。f 實現上的相互依賴性比較小。譯者注 被包含物件與容器物件之間的依賴關係比較少 f 每乙個類只專注於一項任務。f 通過獲取指向其它的具有相同型別的物件引用...

Python的優缺點

python是著名的 龜叔 guido van rossum在1989年聖誕節期間,為了打發無聊的聖誕節而編寫的乙個程式語言。最後說說python的缺點。任何程式語言都有缺點,python也不例外。優點說過了,那python有哪些缺點呢?第乙個缺點就是執行速度慢,和c程式相比非常慢,因為python...