python進階16多繼承與Mixin

原創博客鏈接: python進階16多繼承與Mixin

Mixin解釋

爲了讓大家,對這個 Mixin 有一個更直觀的理解,摘錄了網上一段說明。
民航飛機是一種交通工具,對於土豪們來說直升機也是一種交通工具。對於這兩種交通工具,它們都有一個功能是飛行,但是轎車沒有。所以,我們不可能將飛行功能寫在交通工具這個父類中。但是如果民航飛機和直升機都各自寫自己的飛行方法,又違背了代碼儘可能重用的原則(如果以後飛行工具越來越多,那會出現許多重複代碼)。
怎麼辦,那就只好讓這兩種飛機同時繼承交通工具以及飛行器兩個父類,這樣就出現了多重繼承。這時又違背了繼承必須是”is-a”關係。這個難題該怎麼破?
這時候 Mixin 就閃亮登場了。飛行只是飛機做爲交通工具的一種(增強)屬性,我們可以爲這個飛行的功能單獨定義一個(增強)類,稱之爲 Mixin 類。這個類,是做爲增強功能,添加到子類中的。爲了讓其他開發者,一看就知道這是個 Mixin 類,一般都要求開發者遵循規範,在類名末尾加上 Mixin 。

舉個例子

 

1
2
3
4
5
6
7
8
9
class Vehicle(object):
    pass

class PlaneMixin(object):
    def fly(self):
        print('I am flying')

class Airplane(Vehicle, PlaneMixin):
    pass

使用Mixin類實現多重繼承要遵循以下幾個規範
責任明確:必須表示某一種功能,而不是某個物品;
功能單一:若有多個功能,那就寫多個Mixin類;
絕對獨立:不能依賴於子類的實現;子類即便沒有繼承這個Mixin類,也照樣可以工作,就是缺少了某個功能。

一個 Mixin 類的實例(這個例子並不符合前面的無依賴原則)

這裏,我直接先上代碼,有興趣的同學,可以暫停到這裏,看看這段代碼中的 subclass.display() 這行代碼,究竟是怎麼執行的:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Displayer():
    def display(self, message):
        print(message)


class LoggerMixin():
    def log(self, message, filename='logfile.txt'):
        with open(filename, 'a') as fh:
            fh.write(message)

    def display(self, message):
        super().display(message)
        self.log(message)


class MySubClass(LoggerMixin, Displayer):
    def log(self, message):
        super().log(message, filename='subclasslog.txt')


subclass = MySubClass()
subclass.display("This string will be shown and logged in subclasslog.txt")

代碼不多,也就攏共 22 行,22 行的代碼裏面,定義了 3 個類。其中 MySubClass 多繼承了 LoggerMixin 類和 Displayer 類。看似並沒有什麼異常的代碼裏面,當你嘗試去仔細推敲 subclass.display() 的調用邏輯之後,就變得異常的複雜。
問題:我們的 LoggerMixin 類是怎麼調用的 super().display() 方法的呢?
解答:在多繼承的環境下,super() 有相對來說更加複雜的含義。它會查看你的繼承鏈,使用一種叫做 Methods Resolution Order(方法解析順序) 的方式,來決定調用最近的繼承父類的方法。
也就是說,我們的 MySubClass.display() 調用,觸發了是這麼一系列的行爲:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1. MySubClass.display() is resolved to LoggerMixin.display().
MySubClass.display() 方法被解析爲 LoggerMixin.display() 方法的調用。這應該還是比較好理解的。因爲對於 MySubClass 類來說,在繼承鏈上的兩個父類,LoggerMixin 和 Displayer 來說,LoggerMixin 是最近的,因此調用它的 display() 方法。

2. LoggerMixin.display() calls super().display(), which is resolved to Displayer.display().
LoggerMixin.display() 方法調用了 super().display(),這一行代碼按照我們剛纔的解釋,查看 MySubClass 的繼承鏈,是應該調用 Displayer 類的 display() 方法的。這一步是相對來說比較難以理解的。

讓我們這麼來理解它,當 LoggerMixin.display() 中調用了 super().display() 的時候,它會嘗試去尋找屬於當前類的繼承鏈。而這個當前類是什麼類呢?不是 LoggerMixin 類,而是 MySubClass 類。MySubClass 類的繼承連是 LoggerMixin,然後 Displayer。所以,我們就找到了 Displayer 的 display() 方法。

3. It alse calls self.log(). Since self, in this case, is a MySubClass instance, it resolves to MySubClass.log(). MySubClass.log() calls super().log(), which is resolved back to LoggerMixin.log().
別忘了,我們的 LoggerMixin 類還調用了 self.log() 方法。這個看似好像要直接調用 LoggerMixin 的 log 方法,其實不然。

LoggerMixin 的 display() 方法在當前語境中的 self,其實是 MySubClass 類的對象,因此對於 MySubClass 類的對象,想要調用 log 方法,是直接調用自己類中的 log 方法,也就是 MySubClass.log() 方法,而不是 LoggerMixin.log() 方法的。

而又因爲 MySubClass.log() 方法調用了 super().log() 方法,這才根據繼承鏈尋找最近的父類,才找到了 LoggerMixin 類中的 log() 方法進行調用。

畫圖總結

爲了方便大家理解,我畫了一個簡略的圖方便理解:
一句簡單的 subclass.display() 的背後,究竟發生了什麼?

參考

一個例子走近 Python 的 Mixin 類:利用 Python 多繼承的魔力:https://blog.csdn.net/u012814856/article/details/81355935
1.9 多繼承與Mixin設計模式:python.iswbm.com/en/latest/c01/c01_09.html

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