python学习笔记(八)——继承
前沿
思考
問題一:兩個中有大量重復的代碼,是否能夠只寫一次 ?
問題二: 繼承的意義是什么 ?
面向對象的編程帶來的好處之一是代碼的重用,實現這種重用方法之一是通過繼承機制。
繼承是兩個類或多個類之間的父子關系,子類繼承了基類的所有公有數據屬性和方法,并且可以通過編寫子類的代碼擴充子類的功能。
開個玩笑地說,如果人類可以做到兒女繼承了父母的所有才學并加以拓展,那么人類的發展至少是現在的數萬倍。繼承實現了數據屬性和方法的重用,減少了代碼的冗余度。
那么我們何時需要使用繼承呢?如果我們需要的類中具有公共的成員,且具有一定的遞進關系,那么就可以使用繼承,且讓結構最簡單的類作為基類。一般來說,子類是父類的特殊化,如下面的關系:
哺乳類動物——>狗——>柯基
特定狗種類繼承狗類,狗類繼承哺乳動物類,狗類編寫了描述所有狗種公有的行為的方法而特定狗種類則增加了該狗種特有的行為。
不過繼承也有一定弊端,可能基類對于子類也有一定特殊的地方,如某種特定狗種不具有絕大部分狗種的行為,當程序員沒有理清類間的關系時,可能使得子類具有了不該有的方法。
另外,如果繼承鏈太長的話,任何一點小的變化都會引起一連串變化,我們使用的繼承要注意控制繼承鏈的規模。
繼承
什么是繼承?
繼承就是讓類和類之間轉變為父子關系,子類可以直接訪問(調用)父類的靜態屬性和方法。
在python中,新建的類可以繼承一個或多個父類,父類又可稱為基類或超類,新建的類稱為派生類或子類。
繼承的規則
1、子類繼承父類的成員變量和成員方法
2、子類不繼承父類的構造方法,能夠繼承父類的析構方法
3、子類不能刪除父類的成員,但可以重定義父類成員
4、子類可以增加自己的成員
執行結果:
(<class '__main__.Person'>,) jasn 18 chinese 我說的是普通話!! i want to speak something to yo!!繼承查找的方式跟變量的作用域類似,它會現在當前的類里找,找不到就向父類找,再找不到就再向上找,找不到就報錯。
補充知識
issubclass(A,B),判斷A類是否是B類的子類
isinstance(a,A),判斷a是否是A類的實例對象;
類.__bases__ 查看類的直接父類
a.__mro__ 可以查看繼承順序
object類 是所有類的父類
調用父類方法:
第一種:super().方法() # 祖宗類
第二種:類名.方法(self)
super:調用父類( 是用來解決多重繼承問題的,直接用類名調用父類方法在使用單繼承的時候沒問題,但是如果使用多繼承,會涉及很多的問題)
多繼承
如果有多個基類,則需要全部都寫在括號里,這種情況稱為多繼承。在Python中繼承有以下一些特點:
- 在繼承中基類初始化方法_init_不會被自動調用。如果希望子類調用基類的_init方法,需要在子類的_init方法中顯示調用了它。這與C++和C#區別很大。
- 在調用基類的方法時,需要加上基類的類名前綴,且帶上self參數變量。注意在類中調用該類中定義的方法時不需要self參數。
- Python總是首先查找對應類的方法,如果在子類中沒有對應的方法,Python才會在繼承鏈的基類中按順序查找。
- 在Python繼承中,子類不能訪問基類的私有成員。
實例:
class Base:def play(self):print('我是你祖宗')class A(Base):def play(self):print("我是祖宗的兒子")class B(Base):def play(self):print('我是祖宗的女兒')class C(B,A): # 誰先繼承就用誰passc = C() c.play()執行結果: 我是祖宗的女兒__ new __
__ new __ (cls[,...])的參數,__ new __ 方法的第一個參數是這個類,而其余的參數會在調用成功后全部傳遞給 __ init __ 方法初始化。
所以, __ new __ 方法(第一個執行)先于 __ init __ 方法執行:
我們比較兩個方法的參數,可以發現__new__方法是傳入類(cls),而__init__方法傳入類的實例化對象(self),而有意思的是,__ new __ 方法返回的值就是一個實例化對象(ps:如果__new__方法返回None,則__init__方法不會被執行,并且返回值只能調用父類中的__new__方法,而不能調用毫無關系的類的__new__方法)。
我們可以這么理解它們之間的關系,__new__是開辟疆域的大將軍,而__init__是在這片疆域上辛勤勞作的小老百姓,只有__new__執行完后,開辟好疆域后,__init__才能工作。
實例:
class Base:def __init__(self):print('這是初始化方法里面')def __new__(cls, *args, **kwargs):print('這個cls是:',cls) # cls 就是Base類print('這是在new方法里面')return object.__new__(cls) # 必須有返回值 #實例的時候會先調用_new_方法,然后再調用初始化方法 test = Base()執行結果: 這個cls是: <class '__main__.Base'> 這是在new方法里面 這是初始化方法里面絕大多數情況下,我們都不需要自己重寫__new__方法,但在當繼承一個不可變的類型(例如str類,int類等)時,它的特性就尤顯重要了。我們舉下面這個例子:
INIT
class CapStr(str):def __init__(self, string):self = string.upper()a = CapStr("I love China!") print(a)執行結果: I love China!NEW
class CapStr(str):def __new__(cls, string):string = string.upper()return super().__new__(cls, string)a = CapStr("I love China!") print(a)執行結果: I LOVE CHINA!我們可以根據上面的理論可以這樣分析,我們知道字符串是不可改變的,所以第一個例子中,傳入的字符串相當于已經被打下的疆域,而這塊疆域除了將軍其他誰也無法改變,__init__只能在這塊領地上干瞪眼,此時這塊疆域就是”I love China!“。而第二個例子中,__new__大將軍重新去開辟了一塊疆域,所以疆域上的內容也發生了變化,此時這塊疆域變成了”I LOVE CHINA!“。
小結:__new__和__init__相互配合才是python中真正的類構造器。
單例模式(難點)
單例模式(Singleton Pattern)是一種常用的軟件設計模式,該模式的主要目的是確保某一個類只有一個實例存在。當你希望在整個系統中,某個類只能出現一個實例時,單例對象就能派上用場。
比如,某個服務器程序的配置信息存放在一個文件中,客戶端通過一個 AppConfig 的類來讀取配置文件的信息。如果在程序運行期間,有很多地方都需要使用配置文件的內容,也就是說,很多地方都需要創建 AppConfig 對象的實例,這就導致系統中存在多個 AppConfig 的實例對象,而這樣會嚴重浪費內存資源,尤其是在配置文件內容很多的情況下。事實上,類似 AppConfig 這樣的類,我們希望在程序運行期間只存在一個實例對象。
在 Python 中,我們可以用多種方法來實現單例模式:
方法一:
class Person:pass xiaoming = Person() xiaohong = Person() print(id(xiaoming)) print(id(xiaohong)) 地址是不是都是不一樣的# 單例模式要實現的效果就是--- 每一次實例化所創建的實例都是同一個,內存地址都是一樣的class A:_instance = None # 實例def __new__(cls,*args, **kwargs):if cls._instance == None:cls._instance = object.__new__(cls)return cls._instanceelse:return cls._instancea = A() b = A() print(id(a)) print(id(b))執行結果:(僅參考) 2287413405568 2287413546736 2287413546960 2287413546960方法二:
class Person:def __new__(cls, *args, **kwargs): # self 實例本身 cls 類本身if not hasattr(cls, 'instance'):cls.instance = super().__new__(cls)return cls.instancedef __init__(self, name):self.name = namexiaoming = Person('小明') laowang = Person('老王') print(id(xiaoming)) print(id(laowang)) print(laowang.name) print(xiaoming.name)執行結果: 2096725684296 2096725684296 老王 老王單例的運用:任務管理器 回收站 項目日志 多線程的線程池的設計一般也是采用單例模式
總結
以上是生活随笔為你收集整理的python学习笔记(八)——继承的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言—静态存储与动态存储
- 下一篇: websocket python爬虫_p