教面試官ReentrantLock源碼
960
2025-03-31
繼承
1、什么是繼承
繼承是一種創建新類的方式,在python中,新建的類可以繼承一個或多個父類,父類又可稱為基類或超類,新建的類稱為派生類或子類
python中類的繼承分為:單繼承和多繼承
class?ParentClass1:?#定義父類 ????pass class?ParentClass2:?#定義父類 ????pass class?SubClass1(ParentClass1):?#單繼承,基類是ParentClass1,派生類是SubClass ????pass class?SubClass2(ParentClass1,ParentClass2):?#python支持多繼承,用逗號分隔開多個繼承的類 ????pass
查看繼承
print(SubClass1.__bases__)??#?__base__只查看從左到右繼承的第一個子類,__bases__則是查看所有繼承的父類 print(SubClass2.__bases__)
結果:
D:\YuchuanProjectData\PythonProject\venv\Scripts\python.exe?D:/YuchuanProjectData/PythonProject/YuchuanDemo012.py (
提示:如果沒有指定基類,python的類會默認繼承object類,object是所有python類的基類,它提供了一些常見方法(如__str__)的實現。
print(SubClass1.__bases__)??#?__base__只查看從左到右繼承的第一個子類,__bases__則是查看所有繼承的父類 print(SubClass2.__bases__) print(ParentClass1.__bases__) print(ParentClass2.__bases__)
結果:
D:\YuchuanProjectData\PythonProject\venv\Scripts\python.exe?D:/YuchuanProjectData/PythonProject/YuchuanDemo012.py (
2、繼承與抽象(先抽象再繼承)
抽象即抽取類似或者說比較像的部分。
抽象分成兩個層次:
1.將奧巴馬和梅西這倆對象比較像的部分抽取成類;
2.將人,豬,狗這三個類比較像的部分抽取成父類。
抽象最主要的作用是劃分類別(可以隔離關注點,降低復雜度)
繼承:是基于抽象的結果,通過編程語言去實現它,肯定是先經歷抽象這個過程,才能通過繼承的方式去表達出抽象的結構。
抽象只是分析和設計的過程中,一個動作或者說一種技巧,通過抽象可以得到類
3、繼承與重用性
使用繼承來解決代碼重用的例子
==========================第一部分 例如 貓可以:吃、喝、爬樹 狗可以:吃、喝、看家 如果我們要分別為貓和狗創建一個類,那么就需要為?貓?和?狗?實現他們所有的功能,偽代碼如下: #貓和狗有大量相同的內容 class?貓: ????def?吃(self): ????????#?do?something ????def?喝(self): ????????#?do?something ????def?爬樹(self): ????????#?do?something class?狗: ????def?吃(self): ????????#?do?something ????def?喝(self): ????????#?do?something ????def?看家(self): ????????#do?something ==========================第二部分 上述代碼不難看出,吃、喝是貓和狗都具有的功能,而我們卻分別的貓和狗的類中編寫了兩次。如果使用?繼承?的思想,如下實現: 動物:吃、喝 ???貓:爬樹(貓繼承動物的功能) ???狗:看家(狗繼承動物的功能) 偽代碼如下: class?動物: ????def?吃(self): ????????#?do?something ????def?喝(self): ????????#?do?something #?在類后面括號中寫入另外一個類名,表示當前類繼承另外一個類 class?貓(動物): ????def?爬樹(self): ????????print?'喵喵叫' #?在類后面括號中寫入另外一個類名,表示當前類繼承另外一個類 class?狗(動物): ????def?看家(self): ????????print?'汪汪叫' ==========================第三部分 #繼承的代碼實現 class?Animal: ????def?eat(self): ????????print("%s?吃?"?%self.name) ????def?drink(self): ????????print?("%s?喝?"?%self.name) class?Cat(Animal): ????def?__init__(self,?name): ????????self.name?=?name ????????self.breed?=?'貓' ????def?climb(self): ????????print('爬樹') class?Dog(Animal): ????def?__init__(self,?name): ????????self.name?=?name ????????self.breed='狗' ????def?look_after_house(self): ????????print('汪汪叫') #?#########?執行?######### c1?=?Cat('小白家的小黑貓') c1.eat() c2?=?Cat('小黑的小白貓') c2.drink() d1?=?Dog('胖子家的小瘦狗') d1.eat() 使用繼承來解決代碼重用的例子
在開發程序的過程中,如果我們定義了一個類A,然后又想新建立另外一個類B,但是類B的大部分內容與類A的相同時
我們不可能從頭開始寫一個類B,這就用到了類的繼承的概念。
通過繼承的方式新建類B,讓B繼承A,B會‘遺傳’A的所有屬性(數據屬性和函數屬性),實現代碼重用
class?Animal: ????''' ????人和狗都是動物,所以創造一個Animal基類 ????''' ????def?__init__(self,?name,?aggressivity,?life_value): ????????self.name?=?name??#?人和狗都有自己的昵稱; ????????self.aggressivity?=?aggressivity??#?人和狗都有自己的攻擊力; ????????self.life_value?=?life_value??#?人和狗都有自己的生命值; ????def?eat(self): ????????print('%s?is?eating'%self.name) class?Dog(Animal): ????pass class?Person(Animal): ????pass egg?=?Person('egon',10,1000) ha2?=?Dog('二愣子',50,1000) egg.eat() ha2.eat()
提示:用已經有的類建立一個新的類,這樣就重用了已經有的軟件中的一部分設置大部分,大大省了編程工作量,這就是常說的軟件重用,不僅可以重用自己的類,也可以繼承別人的,比如標準庫,來定制新的數據類型,這樣就是大大縮短了軟件開發周期,對大型軟件開發來說,意義重大.
4、派生
當然子類也可以添加自己新的屬性或者在自己這里重新定義這些屬性(不會影響到父類),需要注意的是,一旦重新定義了自己的屬性且與父類重名,那么調用新增的屬性時,就以自己為準了。
class?Animal: ????''' ????人和狗都是動物,所以創造一個Animal基類 ????''' ????def?__init__(self,?name,?aggressivity,?life_value): ????????self.name?=?name??#?人和狗都有自己的昵稱; ????????self.aggressivity?=?aggressivity??#?人和狗都有自己的攻擊力; ????????self.life_value?=?life_value??#?人和狗都有自己的生命值; ????def?eat(self): ????????print('%s?is?eating'%self.name) class?Dog(Animal): ????''' ????狗類,繼承Animal類 ????''' ????def?bite(self,?people): ????????''' ????????派生:狗有咬人的技能 ????????:param?people:?? ????????''' ????????people.life_value?-=?self.aggressivity class?Person(Animal): ????''' ????人類,繼承Animal ????''' ????def?attack(self,?dog): ????????''' ????????派生:人有攻擊的技能 ????????:param?dog:? ????????''' ????????dog.life_value?-=?self.aggressivity egg?=?Person('egon',10,1000) ha2?=?Dog('二愣子',50,1000) print(ha2.life_value) print(egg.attack(ha2)) print(ha2.life_value)
注意:像ha2.life_value之類的屬性引用,會先從實例中找life_value然后去類中找,然后再去父類中找...直到最頂級的父類。
在子類中,新建的重名的函數屬性,在編輯函數內功能的時候,有可能需要重用父類中重名的那個函數功能,應該是用調用普通函數的方式,即:類名.func(),此時就與調用普通函數無異了,因此即便是self參數也要為其傳值.
在python3中,子類執行父類的方法也可以直接用super方法.
幫你了解super
class?A: ????def?hahaha(self): ????????print('A') class?B(A): ????def?hahaha(self): ????????super().hahaha() ????????#super(B,self).hahaha() ????????#A.hahaha(self) ????????print('B') a?=?A() b?=?B() b.hahaha() super(B,b).hahaha()
class?Animal: ????''' ????人和狗都是動物,所以創造一個Animal基類 ????''' ????def?__init__(self,?name,?aggressivity,?life_value): ????????self.name?=?name??#?人和狗都有自己的昵稱; ????????self.aggressivity?=?aggressivity??#?人和狗都有自己的攻擊力; ????????self.life_value?=?life_value??#?人和狗都有自己的生命值; ????def?eat(self): ????????print('%s?is?eating'%self.name) class?Dog(Animal): ????''' ????狗類,繼承Animal類 ????''' ????def?__init__(self,name,breed,aggressivity,life_value): ????????super().__init__(name,?aggressivity,?life_value)?#執行父類Animal的init方法 ????????self.breed?=?breed??#派生出了新的屬性 ????def?bite(self,?people): ????????''' ????????派生出了新的技能:狗有咬人的技能 ????????:param?people:?? ????????''' ????????people.life_value?-=?self.aggressivity ????def?eat(self): ????????#?Animal.eat(self) ????????#super().eat() ????????print('from?Dog') class?Person(Animal): ????''' ????人類,繼承Animal ????''' ????def?__init__(self,name,aggressivity,?life_value,money): ????????#Animal.__init__(self,?name,?aggressivity,?life_value) ????????#super(Person,?self).__init__(name,?aggressivity,?life_value) ????????super().__init__(name,aggressivity,?life_value)??#執行父類的init方法 ????????self.money?=?money???#派生出了新的屬性 ????def?attack(self,?dog): ????????''' ????????派生出了新的技能:人有攻擊的技能 ????????:param?dog:? ????????''' ????????dog.life_value?-=?self.aggressivity ????def?eat(self): ????????#super().eat() ????????Animal.eat(self) ????????print('from?Person') egg?=?Person('egon',10,1000,600) ha2?=?Dog('二愣子','哈士奇',10,1000) print(egg.name) print(ha2.name) egg.eat()
通過繼承建立了派生類與基類之間的關系,它是一種'是'的關系,比如白馬是馬,人是動物。
當類之間有很多相同的功能,提取這些共同的功能做成基類,用繼承比較好,比如教授是老師
>>>?class?Teacher: ...?????def?__init__(self,name,gender): ...?????????self.name=name ...?????????self.gender=gender ...?????def?teach(self): ...?????????print('teaching') ...? >>>? >>>?class?Professor(Teacher): ...?????pass ...? >>>?p1=Professor('egon','male') >>>?p1.teach() teaching
5、抽象類與接口類
1)、接口類
繼承有兩種用途:
一:繼承基類的方法,并且做出自己的改變或者擴展(代碼重用)
二:聲明某個子類兼容于某基類,定義一個接口類Interface,接口類中定義了一些接口名(就是函數名)且并未實現接口的功能,子類繼承接口類,并且實現接口中的功能
class?Alipay: ????''' ????支付寶支付 ????''' ????def?pay(self,money): ????????print('支付寶支付了%s元'%money) class?Applepay: ????''' ????apple?pay支付 ????''' ????def?pay(self,money): ????????print('apple?pay支付了%s元'%money) def?pay(payment,money): ????''' ????支付函數,總體負責支付 ????對應支付的對象和要支付的金額 ????''' ????payment.pay(money) p?=?Alipay() pay(p,200)
開發中容易出現的問題
class?Alipay: ????''' ????支付寶支付 ????''' ????def?pay(self,money): ????????print('支付寶支付了%s元'%money) class?Applepay: ????''' ????apple?pay支付 ????''' ????def?pay(self,money): ????????print('apple?pay支付了%s元'%money) class?Wechatpay: ????def?fuqian(self,money): ????????''' ????????實現了pay的功能,但是名字不一樣 ????????''' ????????print('微信支付了%s元'%money) def?pay(payment,money): ????''' ????支付函數,總體負責支付 ????對應支付的對象和要支付的金額 ????''' ????payment.pay(money) p?=?Wechatpay() pay(p,200)???#執行會報錯
接口初成:手動報異常:NotImplementedError來解決開發中遇到的問題
class?Payment: ????def?pay(self): ????????raise?NotImplementedError class?Wechatpay(Payment): ????def?fuqian(self,money): ????????print('微信支付了%s元'%money) p?=?Wechatpay()??#這里不報錯 pay(p,200)??????#這里報錯了
借用abc模塊來實現接口
from?abc?import?ABCMeta,abstractmethod class?Payment(metaclass=ABCMeta): ????@abstractmethod ????def?pay(self,money): ????????pass class?Wechatpay(Payment): ????def?fuqian(self,money): ????????print('微信支付了%s元'%money) p?=?Wechatpay()?#不調就報錯了
實踐中,繼承的第一種含義意義并不很大,甚至常常是有害的。因為它使得子類與基類出現強耦合。
繼承的第二種含義非常重要。它又叫“接口繼承”。
接口繼承實質上是要求“做出一個良好的抽象,這個抽象規定了一個兼容接口,使得外部調用者無需關心具體細節,可一視同仁的處理實現了特定接口的所有對象”——這在程序設計上,叫做歸一化。
歸一化使得高層的外部使用者可以不加區分的處理所有接口兼容的對象集合——就好象linux的泛文件概念一樣,所有東西都可以當文件處理,不必關心它是內存、磁盤、網絡還是屏幕(當然,對底層設計者,當然也可以區分出“字符設備”和“塊設備”,然后做出針對性的設計:細致到什么程度,視需求而定)。
依賴倒置原則: 高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;抽象不應該應該依賴細節;細節應該依賴抽象。換言之,要針對接口編程,而不是針對實現編程
在python中根本就沒有一個叫做interface的關鍵字,上面的代碼只是看起來像接口,其實并沒有起到接口的作用,子類完全可以不用去實現接口?,如果非要去模仿接口的概念,可以借助第三方模塊:
http://pypi.python.org/pypi/zope.interface
twisted的twisted\internet\interface.py里使用zope.interface
文檔https://zopeinterface.readthedocs.io/en/latest/
設計模式:https://github.com/faif/python-patterns
為何要用接口
接口提取了一群類共同的函數,可以把接口當做一個函數的集合。 然后讓子類去實現接口中的函數。 這么做的意義在于歸一化,什么叫歸一化,就是只要是基于同一個接口實現的類,那么所有的這些類產生的對象在使用時,從用法上來說都一樣。 歸一化,讓使用者無需關心對象的類是什么,只需要的知道這些對象都具備某些功能就可以了,這極大地降低了使用者的使用難度。 比如:我們定義一個動物接口,接口里定義了有跑、吃、呼吸等接口函數,這樣老鼠的類去實現了該接口,松鼠的類也去實現了該接口,由二者分別產生一只老鼠和一只松鼠送到你面前,即便是你分別不到底哪只是什么鼠你肯定知道他倆都會跑,都會吃,都能呼吸。 再比如:我們有一個汽車接口,里面定義了汽車所有的功能,然后由本田汽車的類,奧迪汽車的類,大眾汽車的類,他們都實現了汽車接口,這樣就好辦了,大家只需要學會了怎么開汽車,那么無論是本田,還是奧迪,還是大眾我們都會開了,開的時候根本無需關心我開的是哪一類車,操作手法(函數調用)都一樣 為何要用接口
2)、抽象類
什么是抽象類
與java一樣,python也有抽象類的概念但是同樣需要借助模塊實現,抽象類是一個特殊的類,它的特殊之處在于只能被繼承,不能被實例化
為什么要有抽象類
如果說類是從一堆對象中抽取相同的內容而來的,那么抽象類就是從一堆類中抽取相同的內容而來的,內容包括數據屬性和函數屬性。
比如我們有香蕉的類,有蘋果的類,有桃子的類,從這些類抽取相同的內容就是水果這個抽象的類,你吃水果時,要么是吃一個具體的香蕉,要么是吃一個具體的桃子。。。。。。你永遠無法吃到一個叫做水果的東西。
從設計角度去看,如果類是從現實對象抽象而來的,那么抽象類就是基于類抽象而來的。
從實現角度來看,抽象類與普通類的不同之處在于:抽象類中有抽象方法,該類不能被實例化,只能被繼承,且子類必須實現抽象方法。這一點與接口有點類似,但其實是不同的,即將揭曉答案
在python中實現抽象類
#一切皆文件 import?abc?#利用abc模塊實現抽象類 class?All_file(metaclass=abc.ABCMeta): ????all_type='file' ????@abc.abstractmethod?#定義抽象方法,無需實現功能 ????def?read(self): ????????'子類必須定義讀功能' ????????pass ????@abc.abstractmethod?#定義抽象方法,無需實現功能 ????def?write(self): ????????'子類必須定義寫功能' ????????pass #?class?Txt(All_file): #?????pass # #?t1=Txt()?#報錯,子類沒有定義抽象方法 class?Txt(All_file):?#子類繼承抽象類,但是必須定義read和write方法 ????def?read(self): ????????print('文本數據的讀取方法') ????def?write(self): ????????print('文本數據的讀取方法') class?Sata(All_file):?#子類繼承抽象類,但是必須定義read和write方法 ????def?read(self): ????????print('硬盤數據的讀取方法') ????def?write(self): ????????print('硬盤數據的讀取方法') class?Process(All_file):?#子類繼承抽象類,但是必須定義read和write方法 ????def?read(self): ????????print('進程數據的讀取方法') ????def?write(self): ????????print('進程數據的讀取方法') wenbenwenjian=Txt() yingpanwenjian=Sata() jinchengwenjian=Process() #這樣大家都是被歸一化了,也就是一切皆文件的思想 wenbenwenjian.read() yingpanwenjian.write() jinchengwenjian.read() print(wenbenwenjian.all_type) print(yingpanwenjian.all_type) print(jinchengwenjian.all_type)
3)、抽象類與接口類
抽象類的本質還是類,指的是一組類的相似性,包括數據屬性(如all_type)和函數屬性(如read、write),而接口只強調函數屬性的相似性。
抽象類是一個介于類和接口之間的一個概念,同時具備類和接口的部分特性,可以用來實現歸一化設計
在python中,并沒有接口類這種東西,即便不通過專門的模塊定義接口,我們也應該有一些基本的概念。
在繼承抽象類的過程中,我們應該盡量避免多繼承;
而在繼承接口的時候,我們反而鼓勵你來多繼承接口
接口隔離原則: 使用多個專門的接口,而不使用單一的總接口。即客戶端不應該依賴那些不需要的接口。
在抽象類中,我們可以對一些抽象方法做出基礎實現;
而在接口類中,任何方法都只是一種規范,具體的功能需要子類實現
6、鉆石繼承
class?A(object): ????def?test(self): ????????print('from?A') class?B(A): ????def?test(self): ????????print('from?B') class?C(A): ????def?test(self): ????????print('from?C') class?D(B): ????def?test(self): ????????print('from?D') class?E(C): ????def?test(self): ????????print('from?E') class?F(D,E): ????#?def?test(self): ????#?????print('from?F') ????pass f1=F() f1.test() print(F.__mro__)?#只有新式才有這個屬性可以查看線性列表,經典類沒有這個屬性 #新式類繼承順序:F->D->B->E->C->A #經典類繼承順序:F->D->B->A->E->C #python3中統一都是新式類 #pyhon2中才分新式類與經典類
python到底是如何實現繼承的,對于你定義的每一個類,python會計算出一個方法解析順序(MRO)列表,這個MRO列表就是一個簡單的所有基類的線性順序列表,例如
>>>?F.mro()?#等同于F.__mro__ [
為了實現繼承,python會在MRO列表上從左到右開始查找基類,直到找到第一個匹配這個屬性的類為止。
而這個MRO列表的構造是通過一個C3線性化算法來實現的。我們不去深究這個算法的數學原理,它實際上就是合并所有父類的MRO列表并遵循如下三條準則:
1.子類會先于父類被檢查
2.多個父類會根據它們在列表中的順序被檢查
3.如果對下一個類存在兩個合法的選擇,選擇第一個父類
7、繼承小結
減少代碼的重用 提高代碼可讀性 規范編程模式
抽象:抽象即抽取類似或者說比較像的部分。是一個從具體到抽象的過程。 繼承:子類繼承了父類的方法和屬性 派生:子類在父類方法和屬性的基礎上產生了新的方法和屬性
1.多繼承問題 在繼承抽象類的過程中,我們應該盡量避免多繼承; 而在繼承接口的時候,我們反而鼓勵你來多繼承接口 2.方法的實現 在抽象類中,我們可以對一些抽象方法做出基礎實現; 而在接口類中,任何方法都只是一種規范,具體的功能需要子類實現
新式類:廣度優先 經典類:深度優先
軟件開發 人工智能 云計算 機器學習
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。