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...