爲什麼要創建一個不能被實例化的類?

花下貓語:今天分享的文章介紹了 Python 中的進階內容 Mixins,文章的切入點很有意思,是從“爲什麼要創建一個不能實例化的類”引入的,全文簡短而清晰,就算是根本沒接觸過這個概念的人,也能基本把握住其要點,值得推薦一讀!

劇照 | 《霸王別姬》

來源:kingname@未聞Code公衆號

當我們創建一個Python 類並初始化時,一般代碼這樣寫:

class People:
    def __init__(self, name):
        self.name = name
    
    def say(self):
        print(f'我叫做:{self.name}')


kingname = People('kingname')
kingname.say()

運行效果如下圖所示:

上面是衆所周知的寫法。但如果有一天,你發現我寫了這樣一個類:

class People:
    def say(self):
        print(f'我叫做:{self.name}')

    def __new__(self):
        raise Exception('不能實例化這個類')


kingname = People()
kingname.say()

一旦初始化就會報錯,如下圖所示:

你會不會感到非常奇怪?一個不能被初始化的類,有什麼用?

這就要引入我們今天討論的一種設計模式——混入(Mixins)。

Python 由於多繼承的原因,可能會出現鑽石繼承[1]又叫菱形繼承。爲了保留多繼承的優點,但又摒除缺點,於是有了混入這種編程模式。

Mixins 是一個 Python 類,它只有方法,沒有狀態,不應該被初始化。它只能作爲父類被繼承。每個 Mixins 類只有一個或者少數幾個方法。不同的 Mixin 的方法互不重疊。

例如,我們現在有一個類 People

class People():
    def __init__(self, name, age):
        self.age = age
        self.name = name

    def say(self):
        print(f'我叫做:{self.name},我今年{self.age}歲')

kingname = People('kingname', 28)
pm = People('pm', 25)
kingname > pm

顯然,這樣寫會報錯,因爲兩個類的實例是不能比較大小的:

但在現實生活中,當我們說 某人比另一個人大時,實際上是指的某人的年齡比另一人年齡大。所以如果要讓這兩個實例比較大小,我們需要實現多個魔術方法:

class People():
    def __init__(self, name, age):
        self.age = age
        self.name = name

    def say(self):
        print(f'我叫做:{self.name},我今年{self.age}歲')
        
    def __ne__(self, other):
        return self.age != other.age
        
    def __lt__(self, other):
        return self.age < other.age
        
    def __le__(self, other):
        return self.age <= other.age
        
    def __gt__(self, other):
        return self.age > other.age
        
    def __ge__(self, other):
        return self.age >= other.age

運行效果如下圖所示:

但如果這幾個魔術方法會在多個類中使用,那麼我們就可以把它抽出來,作爲一個父類:

class ComparableMixin(object):
    def __ne__(self, other):
        return self.age != other.age
    def __lt__(self, other):
        return self.age < other.age
    def __le__(self, other):
        return self.age <= other.age
    def __gt__(self, other):
        return self.age > other.age
    def __ge__(self, other):
        return self.age >= other.age

然後在使用 People 類繼承它:

本質上,混入的寫法與普通的類繼承類沒有什麼區別。但是 在寫 Mixins 類的時候,我們不會寫__init__方法,也不會寫類屬性。並且 Mixin 類中的方法看起來更像是工具方法。

我們可以寫很多個 Mixin 類,然後用一個子類去繼承他們。由於這些 Mixin 類提供的各個工具方法互不相關,所以不存在菱形繼承的問題。但是在子類中卻可以分別調用這些工具方法,從而擴展子類的功能。

最後,我們對比一下抽象類(Abstract Class)接口(Interface)混入(Mixins)的區別:

抽象類:

  • 包含一個或多個抽象方法。

  • 允許包含狀態(實例變量)和非抽象方法。

接口:

  • 只能包含抽象方法。

混入:

  • 不能包含狀態(實例變量)。

  • 包含一個或多個非抽象方法。

參考資料

[1]

鑽石繼承: https://en.wikipedia.org/wiki/Multiple_inheritance

優質文章,推薦閱讀:

微軟官方上線Python教程

Python 工匠:編寫地道循環的兩個建議

如何給列表降維?sum()函數的妙用

Python 進階之源碼分析:如何將一個類方法變爲多個方法?

感謝創作者的好文

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章