我們知道python是一種面向?qū)ο蟮木幊陶Z(yǔ)言,他有對(duì)象這種基礎(chǔ)概念,所以他也就有對(duì)象的三大屬性:多態(tài),繼承和封裝。在談到繼承的時(shí)候我們會(huì)了解到繼承有兩種形式,一種是單繼承,一種是多繼承,今天我們就來(lái)講講python類(lèi)的多繼承模式。
一、Python不同版本的類(lèi)
- Python2.2之前是沒(méi)有共同的祖先的,之后引入Object類(lèi),它是所有類(lèi)的共同祖先類(lèi)Object
- Python2中為了兼容,分為古典類(lèi)(舊式類(lèi))和新式類(lèi)
- Python3中全部都是新式類(lèi)
- 新式類(lèi)都是繼承自O(shè)bject的,新式類(lèi)可以使用super
#古典類(lèi)在python2.x中運(yùn)行
class A: pass
print(dir(A)) # ['__doc__', '__module__']
print(A.__bases__) # ()
a = A()
print(a.__class__) # __main__.A
print(type(a)) # <type 'instance'>
新式類(lèi)
#新式類(lèi)在python3.x中運(yùn)行
class B: pass
print(dir(B)) # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
print(B.__bases__) # (<class 'object'>,)
B = B()
print(B.__class__) # <class '__main__.B'>
print(type(B)) # <class '__main__.B'>
二、多繼承
OCP原則:多用”繼承“,少修改
繼承的用途:增強(qiáng)基類(lèi),實(shí)現(xiàn)多態(tài)
多態(tài)
- 在面向?qū)ο笾?,父?lèi)、子類(lèi)通過(guò)繼承聯(lián)系在一起,如果可以通過(guò)一套方法,就可以實(shí)現(xiàn)不同變現(xiàn),就是多態(tài)
- 一個(gè)類(lèi)繼承自多個(gè)類(lèi)就是多繼承它將具有多個(gè)類(lèi)的特征
三、多繼承弊端
多繼承很好的模擬了世界,因?yàn)槭聞?wù)很少單一繼承,但是舍棄簡(jiǎn)單,必然引入復(fù)雜性,帶來(lái)了沖突
如同一個(gè)孩子繼承了來(lái)自父母雙方的特征,那么到底眼睛像爸爸還是媽媽尼?孩子究竟改像誰(shuí)多一點(diǎn)尼?
多繼承的實(shí)現(xiàn)會(huì)導(dǎo)致編譯器設(shè)計(jì)的復(fù)雜度增加,所以現(xiàn)在很多語(yǔ)言舍棄了類(lèi)的多繼承
C++支持多繼承;Java舍棄了多繼承
- Java中,一個(gè)類(lèi)可以實(shí)現(xiàn)多個(gè)接口,一個(gè)接口也可以繼承多個(gè)接口。Java的接口很純粹,只是方法聲明,繼承者必須實(shí)現(xiàn)這些方法,就具有了這些能力,就能干什么
多繼承可能會(huì)帶來(lái)二義性,例如,貓和狗都繼承自動(dòng)物類(lèi),現(xiàn)在一個(gè)類(lèi)多繼承了貓和狗類(lèi),貓和狗都有了shout方法,子類(lèi)究竟繼承誰(shuí)的shout尼?
- 解決方案:實(shí)現(xiàn)了多繼承的語(yǔ)言,要解決二義性,深度優(yōu)先或者廣度優(yōu)先
四、Python多繼承實(shí)現(xiàn)
class ClassName(基類(lèi)列表): 類(lèi)體
多繼承帶來(lái)的路徑選擇問(wèn)題,究竟繼承那個(gè)父類(lèi)的特征尼?
- 究竟先廣度優(yōu)先,還是深度優(yōu)先
Python使用MRO(method resolution order) 解決類(lèi)搜索順序問(wèn)題。
- 經(jīng)典算法,按照定義從左到右,深度優(yōu)先策略【比如Python2.2之前,左圖的MRO算法,MyClass→D→B→A→C→A】
- 新式類(lèi)算法,經(jīng)典算法的升級(jí),重復(fù)的只保留最后一個(gè)?!咀髨DMRO是:MyClass→D→B→C→A→object】
- C3算法,在類(lèi)被創(chuàng)建出來(lái)的時(shí)候,就計(jì)算除一個(gè)MRO有序列表?!綪ython3唯一支持的算法,左圖中MRO是MyClass→D→B→C→A→object】C3過(guò)于復(fù)雜,沒(méi)必要去記,我們只要記住【object.mro(),顯示繼承的方法,從左到右依次查找】
五、多繼承的缺點(diǎn)
當(dāng)類(lèi)很多,繼承復(fù)雜的情況下,繼承路徑太多,很難說(shuō)清什么樣的繼承路徑
團(tuán)隊(duì)協(xié)作開(kāi)發(fā),如果引入多繼承,那代碼將不可控
不管編程語(yǔ)言是否支持多繼承,都應(yīng)當(dāng)避免多繼承
Pythond的面向?qū)ο?,我們看到太靈活,太開(kāi)放,所以要團(tuán)隊(duì)守規(guī)矩
六、Mixin
類(lèi)有下面的繼承關(guān)系
文檔Document類(lèi)是其他所有文檔類(lèi)的抽象基類(lèi),Word、Pdf類(lèi)是Document的子類(lèi)
需求:為Document子類(lèi)提供打印能力思路:
1、在Document中提供print方法
class Document:
def __init__(self,content):
self.coutent = content
def print(self):
print(self.coutent)
class Word(Document): pass
class Pdf(Document): pass
a = Word("tom com")
a.print() # tom com
基類(lèi)提供的方法不因該具體實(shí)現(xiàn),因?yàn)樗幢剡m合子類(lèi)的打印,子類(lèi)中需要覆蓋重寫(xiě)。
print算是一種能力 -- 打印功能,不是所有的Document的子類(lèi)都需要的,所以,從這個(gè)角度出發(fā)有點(diǎn)問(wèn)題
class Document:
def __init__(self,content):
self.coutent = content
def print(self):
print(self.coutent)
class Word(Document):
def print(self):
print("Word print {}".format(self.coutent))
class Pdf(Document): pass
a = Word("tom com")
a.print() # Word print tom com
思路二:需要打印的子類(lèi)上增加
如果現(xiàn)有子類(lèi)上直接增加,違反了OCP的原則,所以應(yīng)該繼承后增加
class Document: # 不允許修改
def __init__(self,content):
self.coutent = content
def print(self):
print(self.coutent)
class Word(Document): pass # 不允許修改
class Pdf(Document): pass # 不允許修改
class PrinttableWord(Word):
def print(self):
print("PrinttableWord print {}".format(self.coutent))
print(PrinttableWord.mro()) # [<class '__main__.PrinttableWord'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
a = PrinttableWord("tom com")
a.print() # PrinttableWord print tom com
看似不錯(cuò),如果還要提供其他類(lèi)似能力,如何繼承?
應(yīng)用于網(wǎng)絡(luò),文檔應(yīng)該具備序列化的能力,類(lèi)上就應(yīng)該實(shí)現(xiàn)序列化可序列化還可能分為使用pickle、josn、messagepack等
這個(gè)時(shí)候,發(fā)現(xiàn),類(lèi)又可能太多了,繼承的方式不是很好了
功能太多,A類(lèi)需要某幾樣功能,B類(lèi)需要另外幾樣功能,很繁瑣
思路三:裝飾器,用裝飾器增強(qiáng)一個(gè)類(lèi),把功能給類(lèi)附加上去,那個(gè)類(lèi)需要,就裝飾它
def printable(cls):
def _print(self):
print("_print 裝飾器 {}".format(self.coutent))
return _print
cls.print = _print
return cls
class Document:
def __init__(self,content):
self.coutent = content
def print(self):
print(self.coutent)
class Word(Document): pass
class Pdf(Document): pass
@printable
class PrinttableWord(Word): pass #先繼承,后裝飾
print(PrinttableWord.__dict__) # {'__module__': '__main__', '__doc__': None, 'print': <function printable.<locals>._print at 0x0173C228>}
a = PrinttableWord("tom")
a.print() # _print 裝飾器 tom
優(yōu)點(diǎn):簡(jiǎn)單方便,在需要的地方動(dòng)態(tài)增加
思路四:Mixin 【用類(lèi)去繼承】
先看代碼
class PrintableMixin:
def print(self):
print("PrintableMixin {}".format(self.coutent))
class Document:
def __init__(self,content):
self.coutent = content
def print(self):
print(self.coutent)
class Word(Document): pass
class Pdf(Document): pass
class PrinttableWord(PrintableMixin,Word): pass
print(PrinttableWord.mro()) # [<class '__main__.PrinttableWord'>, <class '__main__.PrintableMixin'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
print(PrinttableWord.__dict__) # {'__module__': '__main__', '__doc__': None}
a = PrinttableWord("tom")
a.print() # PrintableMixin tom
Mixin就是其他類(lèi)混合進(jìn)來(lái),同時(shí)帶來(lái)了類(lèi)的屬性和方法
這里看來(lái)Mixin類(lèi)和裝飾器效果一樣,也什么特別的,但是Mixin是類(lèi),就可以繼承,增強(qiáng)功能
class PrintableMixin:
def print(self):
print("PrintableMixin {}".format(self.coutent))
class Document:
def __init__(self,content):
self.coutent = content
def print(self):
print(self.coutent)
class Word(Document): pass
class Pdf(Document): pass
class PrinttableWord(PrintableMixin,Word): pass
class SuperPrintableMixin(PrintableMixin,Word):
def print(self):
print("~"*30)
super(SuperPrintableMixin, self).print()
print("~"*30)
print(SuperPrintableMixin.mro()) # [<class '__main__.SuperPrintableMixin'>, <class '__main__.PrintableMixin'>, <class '__main__.Word'>, <class '__main__.Document'>, <class 'object'>]
print(SuperPrintableMixin.__dict__) # {'__module__': '__main__', 'print': <function SuperPrintableMixin.print at 0x018264B0>, '__doc__': None}
a = SuperPrintableMixin("tom")
a.print() # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# PrintableMixin tom
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
七、Mixin類(lèi)
Minxin本質(zhì)上就是多繼承實(shí)現(xiàn)的
Mixin體現(xiàn)的是一種組合的設(shè)計(jì)模式
在面向?qū)ο蟮脑O(shè)計(jì)中,一個(gè)負(fù)載的類(lèi),往往需要很多功能,而這些功能有來(lái)自不同的類(lèi)提供,這就需要很多的類(lèi)組合在一起
從設(shè)計(jì)模式的角度來(lái)說(shuō),多組合,少繼承。
- Mixin類(lèi)的使用原則
- Mixin類(lèi)中不應(yīng)該顯示的出現(xiàn)__init__初始化方法
- Mixin類(lèi)通常不能獨(dú)立工作,因?yàn)樗菧?zhǔn)備混入別的類(lèi)中的部分功能實(shí)現(xiàn)
- Mixin類(lèi)的祖先類(lèi)也應(yīng)該是Mixin類(lèi)
- 使用時(shí),Mixin類(lèi)通常在繼承列表第一個(gè)位置,例如:class PrintableWord(PrintableMixin,Word):pass
以上就是Python類(lèi)的多繼承知識(shí)的詳細(xì)總結(jié)了,希望能給各位小伙伴一個(gè)參考,也希望大家以后多多支持W3Cschool!