流暢的python讀書筆記-第十章-繼承優缺點

繼承的優缺點

推出繼承的初衷是讓新手順利使用只有專家才能設計出來的框架。
——Alan Kaypython

子類化內置類型很麻煩 (如 list 或 dict)) ,別搞這種

  1. 直接子類化內置類型(如 dict、list 或 str)容易出錯,由於內置類型的 方法一般會忽略用戶覆蓋的方法。
  2. 不要子類化內置類型,用戶本身定義的類應該繼承 collections 模塊的類,
  3. 例如UserDict、UserList 和 UserString,這些類作了特殊設計,所以易於擴展。
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"])
  • 綜上,本節所述的問題只發生在 C 語言實現的內置類型內部的方法委託上,並且隻影響 直接繼承內置類型的用戶自定義類。
  • 若是子類化使用 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)

#看繼承關係
print(D.__mro__)

直接調用 d.pong() 運行的是 B 類中的版本。程序員

Python 能區分 d.pong() 調用的是哪一個方法,是由於 Python 會按照特定的順序遍歷繼承圖。
這個順序叫方法解析順序(Method Resolution Order,MRO)。
類都有一個名爲__mro__ 的屬性,它的值是一個元組,按照方法解析順序列出各個超類,從當前類一直向上,直到 object 類。D

然而,使用 super() 最安全,也不易過期。調用框架或不受本身控制的類層次結構中的
方法時,尤爲適合使用 super()。設計模式

多重繼承的真實應用

1 多重繼承能發揮積極做用。
2 《設計模式:可複用面向對象軟件的基礎》一書中的適配器模式用的就是多重繼承,所以使用多重繼承確定沒有錯
3(那本書中的其餘 22 個設計模式都使用單繼承,所以多重繼承顯然不是靈丹妙藥)安全

處理多重繼承

下面是避免把類圖攪亂的一些建議。app

01. 把接口繼承和實現繼承區分開

使用多重繼承時,必定要明確一開始爲何建立子類。主要緣由可能有:框架

繼承接口,建立子類型,實現「是什麼」關係
繼承實現,經過重用避免代碼重複

其實這兩條常常同時出現,不過只要可能,必定要明確意圖。經過繼承重用代碼是實
現細節,一般能夠換用組合和委託模式。而接口繼承則是框架的支柱。post

02. 使用抽象基類顯式表示接口

現代的 Python 中,若是類的做用是定義接口,應該明確把它定義爲抽象基類。Python
3.4 及以上的版本中,咱們要建立 abc.ABC 或其餘抽象基類的子類

python沒有interface這種定義設計

03. 經過混入重用代碼

  • 一個類的做用是爲多個不相關的子類提供方法實現
  • 應該把那個類明確地定義爲混入類(mixin class)
  • 從概念上講,混入不定義新類型,只是打包方法,便於重用。
  • 混入類絕對不能實例化,並且具體類不能只繼承混入類。
  • 混入類應該提供某方面的特定行爲,只實現少許關係很是緊密的方法。

04. 在名稱中明確指明混入

  • 由於在 Python 中沒有把類聲明爲混入的正規方式,因此強烈推薦在名稱中加入...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 類的定義體是空的,可是這個類提供了有用的服務:code

把四個超類結合在一塊兒,這樣須要建立新小組件的用戶無需記住所有混入,也不用擔憂聲明 class 語句時有沒有遵照特定的順序。

08. 「優先使用對象組合,而不是類繼承」

這句話引自《設計模式:可複用面向對象軟件的基礎》一書, 這是我能提供的最佳
建議。對象

熟悉繼承以後,就太容易過分使用它了。出於對秩序的訴求,咱們喜歡按整潔
的層次結構放置物品,程序員更是樂此不疲。

即使是單繼承,這個原則也能提高靈活性,由於子類化是
一種緊耦合,並且較高的繼承樹容易倒。

繼承在Django的應用

page 417 這裏有些複雜,等我牛掰了再來看

總結

collections.abc 模塊中相應的抽象基類
多重繼承這把雙刃劍。首先,咱們說明了 mro 類屬性中蘊藏的方法解析順序,有了這一機制,繼承方法的名稱再也不會發生衝突不要子類化內置類型,用戶本身定義的類應該繼承 collections 模塊的類

相關文章
相關標籤/搜索