網站首頁 編程語言 正文
前言
最近在看sanic的源碼,發現有很多Mixin的類,大概長成這個樣子
class BaseSanic( ? ?RouteMixin, ? ?MiddlewareMixin, ? ?ListenerMixin, ? ?ExceptionMixin, ? ?SignalMixin, ? ?metaclass=SanicMeta, ):
于是對于這種 Mixin 研究了一下,其實也沒什么新的東西,Mixin 又稱混入,只是一種編程思想的體現,但是在使用過程中還是有一些需要注意的地方。 大家都知道,python 是一種允許多繼承的語言,一個類可以繼承多個類,這和java不太一樣,java類只能有一個父類, 但是java 中有接口的概念,一個類可以實現多個接口,但是java的接口只是定義的函數的簽名,并沒有具體的實現,具體的實現需要相應的類來完成。 python 就不一樣了,一個類可以有多個父類,而混入類就是這種允許多繼承語言中才有的一種編程模式。 為了更好的理解混入,我們舉一個生活中的例子---手機, 手機有很多功能,由不同的硬件組合而成,手機有接打電話,收發短信,上網,聽歌等功能,組裝一臺手機就需要將各種硬件進行拼接。 如果我們把這些功能抽象成類,那么我們可以有以下寫法,為了簡單一點,只列接打電話,收發短信功能。
class Tel: ? ?def telfunc(self): ? ? ? ?print("我可以接打電話") ? ? ? ?class SMS: ? ?def smsfunc(self): ? ? ? ?print("我可以發短信") ? ? ? ?class Phone: ? ?def __init__(self, sn): ? ? ? ?self.sn = sn
上面的代碼中, 有三個類,Tel 類,它有一個?telfunc
?方法用于表示有接打電話的能力(或者說是功能), SMS 類有smsfunc
表示SMS類有發短信的能力。 而 Phone 這個類才是一個手機類,它應該具有接打電話和發送短信的能力,但是如果我們用上面的方式定義Phone 這個類,則這個類并沒有接打電話和收發短信的能力。 我們可以怎樣做讓Phone這個類可以具有打電話和發短信的能力? 我們可以在Phone 這個類里再重新定義二個方法?telfunc
和?smsfunc
,也就是將Tel類和SMS類里的方法再寫一遍,這種其實不符合?don't repeate youself
的理念。 正常情況下我們是讓Phone這個類繼承Tel類和SMS類,這樣Phone這個類就自動擁有了接打電話和發短信的能力了。
class Tel: ? ?def telfunc(self): ? ? ? ?print("我可以接打電話") ?class SMS: ? ?def smsfunc(self): ? ? ? ?print("我可以發短信") ? ?class Phone(Tel, SMS): ? ?def __init__(self, sn): ? ? ? ?self.sn = sn ? ? ?def welcome(self): ? ? ? ?print("welcome {}".format(self.sn)) ?p = Phone("xiaomi") p.telfunc() p.smsfunc() p.welcome() ? ''' 我可以接打電話 我可以發短信 welcome xiaomi '''
像這種類的定義就是我們所說的混入,將通話功能與短信功能加入到手機中,讓手機擁有接打電話和發送短信的功能,這種混入的編碼思想有時可以減少很多代碼量。很方便的根據一個類需要哪些功能就將哪個類“混入”到該類中。 通常情況下,我們會將混入類的命名以Mixin結尾,像上面的代碼我們會寫成下面這樣
class TelMixin: ? ?def telfunc(self): ? ? ? ?print("我可以接打電話") ?class SMSMixin: ? ?def smsfunc(self): ? ? ? ?print("我可以發短信") ?class Phone(TelMixin, SMSMixin): ? ?def __init__(self, sn): ? ? ? ?self.sn = sn ? ? ?def welcome(self): ? ? ? ?print("welcome {}".format(self.sn))
以 Mixin 結尾的類,一般是那種功能比較單一,且一般都是某一類型的功能, 如最開始介紹的sanic混入類 RouteMixin 與路由相關的功能類,MiddlewareMixin 是與中間件相關的類,ListenerMixin 是監聽器相關的類,這些類里的方法專注于自己相關的功能,如果有哪個類需要這些功能,那定義的時候就繼承自這些類。
有人會問了,我不想多繼承,管理MRO 太麻煩了,我只想單繼承,我定義一個統一的父類,它即有接打電話,也有收發短信的功能,還可以聽歌,然后讓手機來繼承這個類不好嗎?
如下面的代碼:
class Tel: ? ?def telfunc(self): ? ? ? ?print("我可以接打電話") ? ?def smsfunc(self): ? ? ? ?print("我可以發短信") ? ?def songfunc(self): ? ? ? ?print("我可以放音樂") ? ?class Phone(Tel): ? ?def __init__(self, sn): ? ? ? ?self.sn = sn ? ? ?def welcome(self): ? ? ? ?print("welcome {}".format(self.sn))
首先這種寫法當然是可以的,語法上沒有任何問題,也很好的實現了相應的功能,代碼量也減少了,但是這里大家想一個邏輯問題,如果我想造一個ipod類,ipod 這個類沒有接打電話收發短信的功能,只有聽歌的功能,那么我此時要寫這個類該如何定義? 是不是要定義一個ipod類,然后再寫它的聽歌方法songfunc,如果此時使用混入的編程思想,那么我們就完全可以定義ipod類的時候?不加入?接打電話和收發短信的類就可以了。
class TelMixin: ? ?def telfunc(self): ? ? ? ?print("我可以接打電話") ?class SMSMixin: ? ?def smsfunc(self): ? ? ? ?print("我可以發短信") ? ? ? ?class SongMixin: ? ?def songfunc(self): ? ? ? ?print("我可以放音樂") ?class Phone(TelMixin, SMSMixin, SongMixin): ? ?def __init__(self, sn): ? ? ? ?self.sn = sn ?class Ipod(SongMixin): ? ?def __init__(self, sn): ? ? ? ?self.sn = sn
如果還有別的什么類,比如對講機,它只有接打電話的功能,那么我們就只需要把接打電話的功能混入到對講機類即可,?class Intercom(TelMixin):
但是有人又會問了,這樣的混入,雖說少寫了一些代碼,但是如果子類相應實現并不完全和父類一致該如何處理? 如對講機雖然可以通話,但是它是單向通話,并不能雙向通話的。 這時對于子類實現與父類不一致的情況,那么就需要子類重寫父類方法了,也就是OOP中的繼承與多態,嚴格意義上來說, python 并沒有多態,或者說它天然的就是多態。
class TelMixin: ? ?def telfunc(self): ? ? ? ?print("我可以接打電話") ?class SMSMixin: ? ?def smsfunc(self): ? ? ? ?print("我可以發短信") ?class SongMixin: ? ?def songfunc(self): ? ? ? ?print("我可以放音樂") ?class Intercom(TelMixin): ? ?def __init__(self, sn): ? ? ? ?self.sn = sn ? ? ?def telfunc(self): ? ? ? ?print("對講機{} 的通話".format(self.sn)) ?class Phone(TelMixin, SMSMixin, SongMixin): ? ?def __init__(self, sn): ? ? ? ?self.sn = sn ?class Ipod(SongMixin): ? ?def __init__(self, sn): ? ? ? ?self.sn = sn ?def testTelfunc(o): ? ?o.telfunc() d = Intercom("moto") p = Phone("huawei") ipod = Ipod("apple") testTelfunc(d) testTelfunc(p) testTelfunc(ipod)
以上代碼,我們重寫了對講機類的telfunc 方法,但是并沒有對Ipod類實現通話功能, 然后寫了一個testTelfunc(o)
?方法來調用傳入參數的通話功能,
得到以下輸出:
#對講機moto 的通話
#我可以接打電話
Traceback (most recent call last):
??File "/Users/yyx/test/test.py", line 54, in <module>
????testTelfunc(ipod)
??File "/Users/yyx/test/test.py", line 46, in testTelfunc
????o.telfunc()
AttributeError: 'Ipod' object has no attribute 'telfunc'
當給testTelfunc 傳入的是對講機Intercom 對象時,由于該類重寫了telfunc方法,所以這里調用的是該子類的telfunc方法,當傳入的是Phone類對象時,由于該類繼承了TelMixin類,且沒有重寫telfunc方法,所以這里會調用TelMixin類中的telfunc 方法,但是當傳入的是Ipod類時,這個類既沒有繼承TelMixin,也沒有自己的telfunc方法,所以就崩潰了。這也是一種另類的多態體現吧,只是它沒有像java中的那么嚴格。
Mixin 與繼承的區別
說了那么多的Mixin混入,其實它本質上就是繼承,只是這種繼承是存在于允許多繼承的編程語言中,如果說區別,本質上也沒有什么區別,如果硬是要說些區別,其實也是有一點點。
- 首先,Mixin 類最好不要有初始化方法,也就是?
__init__
?方法,因為如果Mixin類定義了初始化方法,那么在子類中也最好調用一下父類也就是混入類的初始方法super().__init__()
,當然不調用也沒關系,IDE 只會給你一個提示,語法上并沒有什么錯誤。 - Mixin 類最好不要單獨的實例化,因為混入類的設計初衷就是要讓子類來繼承的,然后子類可以豐富混入類的功能,混入類只是為了讓子類擁有某些功能而來的。
- Mixin 類最好是某一類功能的合集,比如上面sanic 源碼中的 RouteMixin 混入類,它只定義與路由相關的功能方法,這樣的好處是,不會有同名的方法,如果混入類中有同名的方法,那么就給自己找麻煩了,排查問題也不太好排查,其實這個也是比單繼承自一個類方便的地方。
說了那么多Mixin 的好處,它有什么不好的地方嗎? 其實在我看來,有一個問題,也說不上是不好,只是習慣問題,以前寫代碼還是寫單繼承多一些,并不太習慣這種多繼承的方式,不過如果一切都按照相應的規則來操作也不是什么大問題。 還有一個問題就是功能的拆分,這個很是考驗編程人員在設計代碼之初整體把握能力,就像單體應用想要拆分成微服務,邊界問題可能是開始時最頭疼的,拆細了吧,會寫出很多類,拆粗了吧,就又回到單繼承的思維模式了,所以這個也是個經驗問題吧。
總結
Mixin 混入也可以說是編程模式,并不是什么新的語法,用好混入類可以使自己的代碼結構清晰,功能明了,所以以后在設計類時要多考慮使用Mixin混入類的實現方式。
原文鏈接:https://juejin.cn/post/7120259883584192549
相關推薦
- 2022-08-03 Android開發手冊Chip監聽及ChipGroup監聽_Android
- 2022-04-15 Android開發Jetpack組件Room用例講解_Android
- 2022-03-16 詳解c#中Array,ArrayList與List<T>的區別、共性與相互轉換_C#教程
- 2022-12-09 Python網絡編程之Python編寫TCP協議程序的步驟_python
- 2023-02-04 golang?Gorm框架講解_Golang
- 2022-11-28 Flutter?Widgets之標簽類控件Chip詳解_IOS
- 2022-04-11 jackson中對null的處理
- 2022-12-29 Kotlin使用滾動控件RecyclerView實例教程_Android
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支