Python面向?qū)ο缶幊袒A(chǔ)解析(一)
1.什么是面向?qū)ο?/strong>
面向?qū)ο螅╫op)是一種抽象的方法來理解這個世界,世間萬物都可以抽象成一個對象,一切事物都是由對象構(gòu)成的。應(yīng)用在編程中,是一種開發(fā)程序的方法,它將對象作為程序的基本單元。
2.面向?qū)ο笈c面向過程的區(qū)別
我們之前已經(jīng)介紹過面向過程了,面向過程的核心在‘過程'二字,過程就是解決問題的步驟,面向過程的方法設(shè)計程序就像是在設(shè)計一條流水線,是一種機械式的思維方式
優(yōu)點:復(fù)雜的問題簡單化,流程化
缺點:擴展性差
主要應(yīng)用場景有:Linux內(nèi)核,git,以及http服務(wù)
面向?qū)ο蟮某绦蛟O(shè)計,核心是對象,對象就是特征(變量)與技能(函數(shù))的結(jié)合體。
優(yōu)點:解決了程序擴展性差的問題
缺點:可控性差,無法預(yù)測最終結(jié)果
主要應(yīng)用場景是需求經(jīng)常變化的軟件,即與用戶交互比較頻繁的軟件
需要注意的是:面向?qū)ο蟮某绦蛟O(shè)計并不能解決全部問題,只是用來解決擴展性。當(dāng)然,現(xiàn)在的的互聯(lián)網(wǎng)軟件,擴展性是最重要的
3.對象與類的概念
在python中,一切皆對象,一個對象應(yīng)該具有自己的屬性,也就是特征,還有有自己的功能,即方法
在Python中,特征用變量表示,功能用函數(shù)表示,所以對象就是變量與函數(shù)的結(jié)合體
而從各種各樣的對象中抽取出來具有相同特征和相同功能組成的,就是類,所以說類是一系列對象共同特征與功能的結(jié)合體
下面讓我們來定義一個類,方法與定義一個函數(shù)有些類似:
#定義一個中國人的類 class Chinese: #共同的特征 country='China' #共同的技能 def talk(self): print('is talking Chinese') def eat(self): print('is eating Chinese food')
這樣我們就定義好了一個類,注意:
1.定義類用class關(guān)鍵字
2.類名一般首字母大寫,且冒號前面不需要括號(非必須,有括號也不報錯,一般需要繼承object類來保證是新式類),區(qū)別于函數(shù)定義
3.與函數(shù)不同,類在定義階段就會執(zhí)行類里面的代碼
4.類有兩種屬性,共同的特征叫數(shù)據(jù)屬性,共同的功能叫函數(shù)屬性
怎樣由這個類產(chǎn)生一個對象呢?實例化:
#實例化的方式產(chǎn)生一個對象 p1=Chinese() p2=Chinese()
我們可以得出結(jié)論了,不管現(xiàn)實世界中怎么樣,但是在程序中,確實是先有類,才有的對象
我們已經(jīng)通過實例化的方式得到兩個對象了,但是有一個問題,得到的兩個對象,特征和功能都是一樣的,這根萬物皆對象的理念完全不符啊,應(yīng)該是每個對象都是不同的,這樣的世界才有意思啊
事實上,我們在定義類的時候,忘記了定義 __init__() 這個函數(shù),正確的定義方法應(yīng)該是這樣的:
#定義一個中國人的類 class Chinese: #共同的特征 country='China' #初始化 def __init__(self,name,age): self.name=name #每個對象都有自己的名字 self.age=age #每個對象都有自己的年齡 #共同的技能 def talk(self): print('is talking Chinese') def eat(self): print('is eating Chinese food') #實例化的方式產(chǎn)生一個對象 p1=Chinese('zhang',18)
類名加括號就是實例化,實例化就會自動觸發(fā)__init__ 函數(shù)運行,可以用它來為每個對象定制自己的特征
我們在定義__init__函數(shù)的時候,括號里有三個參數(shù),但是我們實例化調(diào)用的時候卻只傳了兩個值,為什么不報錯呢?這是因為self的作用就是:實例化的時候,自動將對象本身傳給__init__函數(shù)的第一個參數(shù),當(dāng)然self只是個名字了。
注意:這種自動傳值的機制只是在實例化的時候才會體現(xiàn),類除了實例化還有一種作用就是屬性引用,方法是類名.屬性
從上面報錯的代碼可以看出,屬性引用的時候,沒有自動傳值這回事,如果是類調(diào)用類里面的方法,需要手動把類當(dāng)作參數(shù)傳給它
Chinese.talk(Chinese)
我們學(xué)過名稱空間的概念,定義一個變量,或者定義一個函數(shù)都會在內(nèi)存中開辟一塊內(nèi)存空間,類里面也有定義變量(數(shù)據(jù)屬性),定義函數(shù)(函數(shù)屬性),他們也有名稱空間,可以通過.__dict__的方法查看
p1=Chinese('zhang',18) print(Chinese.__dict__) #{'__module__': '__main__', 'country': 'China', '__init__': <function Chinese.__ # init__ at 0x000002187F35D158>, 'talk': <function Chinese.talk at 0x000002187F35D1E0>, # 'eat': <function Chinese.eat at 0x000002187F35D268>, '__ # dict__': <attribute '__dict__' of 'Chinese' objects>, # '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None} print(p1.__dict__) #{'name': 'zhang', 'age': 18}
通過上面代碼顯示的結(jié)果我們知道了,打印實例化后的對象的名稱空間,只顯示自己特有的屬性,如果想要找到和其他對象共有的屬性,就要去類的名稱空間里面去找
還有一個問題,對象的名稱空間中沒有函數(shù)屬性,當(dāng)然也是去類里面找,但是不同對象指定的函數(shù),是一個函數(shù)嗎
p1=Chinese('zhang',18) p2=Chinese('li',19) print(Chinese.talk)#<function Chinese.talk at 0x000001B8A5B7D1E0> print(p1.talk) #<bound method Chinese.talk of <__main__.Chinese object at 0x000001B8A5B7BD68>> print(p2.talk) #<bound method Chinese.talk of <__main__.Chinese object at 0x000001B8A5B7BDA0>>
可以看到,并不是,他們的內(nèi)存地址都不一樣。而且注意bound method,是綁定方法
對象本身只有數(shù)據(jù)屬性,但是Python的class機制將類的函數(shù)也綁定到對象上,稱為對象的方法,或者叫綁定方法。綁定方法唯一綁定一個對象,同一個類的方法綁定到不同的對象上,屬于不同的方法。我們可以驗證一下:
當(dāng)用到這個函數(shù)時:類調(diào)用的是函數(shù)屬性,既然是函數(shù),就是函數(shù)名加括號,有參數(shù)傳參數(shù)
而對象用到這個函數(shù)時,對象沒有函數(shù)屬性,他是綁定方法,綁定方法怎么用呢,也是直接加括號,但不同的是,綁定方法會默認把對象自己作為第一個參數(shù)
class Chinese: country='China' def __init__(self,name,age): self.name=name self.age=age def talk(self): print('%s is talking Chinese'%self.name) def eat(self): print('is eating Chinese food') p1=Chinese('zhang',18) p2=Chinese('li',19) Chinese.talk(p1) #zhang is talking Chinese p1.talk() #zhang is talking Chinese
只要是綁定方法,就會自動傳值!其實我們以前就接觸過這個,在python3中,類型就是類。數(shù)據(jù)類型如list,tuple,set,dict這些,實際上也都是類,我們以前用的方法如l1.append(3),還可以這樣寫:l1.append(l1,3)
繼承與派生
我們已經(jīng)說過,Python中一切皆對象。我們從對象中抽取了共同特征和技能,得到了類的概念。類與類之間也有共同特征,我們可以從有共同特征和技能的類中提取共同的技能和特征,叫做父類。
比如老師和學(xué)生,都有名字,年紀,生日,性別等等,都會走,說話,吃飯。。。我們就可以從老師和學(xué)生中總結(jié)出來一個‘人'類,稱為父類,那老師和學(xué)生就是‘人'類的子類,子類繼承父類,就有了父類的特征和方法。
繼承是一種什么‘是'什么的關(guān)系,繼承是一種產(chǎn)生新類的方法,當(dāng)然目的也是為了減少代碼重用。
繼承的基本形式是:
class People: pass class Student(People):#People稱為基類或者父類 pass
1.在Python中支持多繼承,一個子類可以繼承多個父類
我們可以通過__bases__的方法查看繼承的所有父類,會返回一個元組。
class People: pass class Animals: pass class Student(People,Animals): pass print(Student.__bases__)#(<class '__main__.People'>, <class '__main__.Animals'>) print(People.__bases__)#(<class 'object'>,)
可以看到,在People父類中,默認也繼承了一個object類,這就是新式類和經(jīng)典類的區(qū)別:
凡是繼承了object類的類及其子類,都稱為新式類,沒有繼承object類的類,稱為經(jīng)典類。
在Python 3中,默認就是新式類,而在Python2.X中,默認都是是經(jīng)典類
繼承怎么減少代碼呢?看例子
class People: def __init__(self,name,age): self.name=name self.age=age def walk(self): print('%s is walkig'%self.name) class Teacher(People): def __init__(self,name,age,level): People.__init__(self,name,age) self.level=level t1=Teacher('zhang',18,10) print(t1.level) #10 print(t1.name) #zhang 子類可以用父類定義的屬性 t1.walk() #zhang is walking 子類無需定義就可以用父類的方法 print(issubclass(Teacher,People)) #True查看Teacher類是不是People類的子類
從上面的例子中可以看到,Teacher類繼承了父類People類,但是Teacher又有自己特有的屬性level,子類也可以定義自己獨有的方法,甚至可以和父類的方法重名,但是執(zhí)行時會以子類定義的為準。
這就叫做派生
2.組合
繼承是解決什么‘是'什么的問題,那還有一種場景就是什么有什么,比如老師有生日,學(xué)生也有生日,生日有年月日這些屬性,如果每個類都寫的話,又是重復(fù)代碼。但是又不能讓學(xué)生和老師繼承生日類。這時就用到了組合。組合就是解決什么‘有'什么的問題。看例子
class Date: def __init__(self,year,mon,day): self.year=year self.mon=mon self.day=day def tell_birth(self): print('出生于%s年%s月%s日'%(self.year,self.mon,self.day)) class Teacher: def __init__(self,name,age,year,mon,day): self.name=name self.age=age self.birth=Date(year,mon,day) t=Teacher('egon',19,2010,10,10) print(t.birth) #<__main__.Date object at 0x0000017E559380F0> t.birth.tell_birth() #出生于2010年10月10日
什么?嫌參數(shù)太多?*args學(xué)過吧,你高興就好
class Date: def __init__(self,year,mon,day): self.year=year self.mon=mon self.day=day def tell_birth(self): print('出生于%s年%s月%s日'%(self.year,self.mon,self.day)) class Teacher: def __init__(self,name,age,*args): self.name=name self.age=age self.birth=Date(*args) t=Teacher('egon',19,2010,10,10) print(t.birth) #<__main__.Date object at 0x0000017E559380F0> t.birth.tell_birth() #出生于2010年10月10日
3.抽象類與接口
繼承有兩種用途:
1.代碼重用,子類繼承父類的方法
2.聲明某個子類兼容于某父類,定義一個接口類Interface,接口類中定義了一些接口名(就是函數(shù)名)且并未實現(xiàn)接口的功能,子類繼承接口類,并且實現(xiàn)接口中的功能
需要注意的是,Python中并沒有接口的關(guān)鍵字,我們只能是模仿接口的功能
比如在 Python中,一切皆文件嘛,那程序是文件,硬件是文件,文本文檔也是文件,我們知道什么叫文件呢,就是能讀能寫,那程序,文本文檔這些,都應(yīng)該有讀和寫的功能,我們來模擬一下
class Interface: def read(self): pass def write(self): pass class Txt(Interface): def read(self): print('文本文檔的讀取方式') def write(self): print('文本文檔的寫入方式') class Sata(Interface): def read(self): print('硬盤文件的讀取方式') def write(self): print('硬盤文件的寫入方式') class process(Interface): def read(self): print('進程數(shù)據(jù)的讀取方式') def write(self): print('進程數(shù)據(jù)的寫入方式')
這么做的意義就是:我們不需要知道子類有什么具體的方法,既然他們繼承了文件類,那他們就是文件,那他們就有讀和寫這兩個功能
父類限制了子類子類必須有read和write這兩個方法,而且名字也必須一樣(當(dāng)然現(xiàn)在只是我們主觀上的限制,一會我們說完抽象類,就可以從代碼級別上限制了),這樣就實現(xiàn)了統(tǒng)一,模擬了接口的概念,這就是歸一化設(shè)計。在歸一化設(shè)計中,只要是基于一個接口設(shè)計的類,那么所有的這些類實例化出來的對象,在用法上是一樣的
我們再來說一下抽象類:
Python中的抽象類需要導(dǎo)入一個模塊來實現(xiàn)。抽象類只能被繼承,不能被實現(xiàn)
抽象類的寫法:
import abc class File(metaclass=abc.ABCMeta): @abc.abstractmethod def read(self): pass @abc.abstractmethod def write(self): pass #父類使用了抽象類,那子類就必須繼承父類的方法,而且名字也必須一樣 #這樣就實現(xiàn)了代碼級別的限制 class Txt(File): def read(self): print('文本文檔的讀取方式') def write(self): print('文本文檔的寫入方式')
繼承原理:
當(dāng)我們定義一個類后,Python就會根據(jù)上面的繼承規(guī)律解析出一個繼承順序的列表(MRO列表),可以通過mro()查看,但是這個方法只有在新式類中才有,經(jīng)典類沒有
super()方法
我們之前用繼承是怎么用的來著,
class Parent(object): def __init__(self,name,age): self.name=name self.age=age class Child(Parent): def __init__(self,name,age,salary): Parent.__init__(self,name,age,salary) self.salary=salary
這其實是和繼承沒啥關(guān)系的寫法,如果父類名字改了,在子類中也要改,更優(yōu)雅的寫法是用super()
class Parent(object): def __init__(self,name,age): self.name=name self.age=age class Child(Parent): def __init__(self,name,age,salary): super().__init__(name,age) self.salary=salary
這是python3中的寫法,如果是python2,super后面的括號里要寫(Child,self)
注意:super()方法只適用于新式類
如果是多繼承的關(guān)系,就用到mro列表,如果就是要繼承多個父類的方法,那就還是乖乖的用以前指名道姓的方法引用
看完這篇,可以繼續(xù)參閱:
總結(jié)
以上就是本文關(guān)于Python面向?qū)ο缶幊袒A(chǔ)解析的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:Python探索之ModelForm代碼詳解、Python_LDA實現(xiàn)方法詳解等,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
相關(guān)文章
python sklearn中tsne算法降維結(jié)果不一致問題的解決方法
最近在做一個文本聚類的分析,在對文本數(shù)據(jù)embedding后,想著看下數(shù)據(jù)的分布,于是用sklearn的TSNE算法來降維embedding后的數(shù)據(jù)結(jié)果,當(dāng)在多次執(zhí)行后,竟發(fā)現(xiàn)TSNE的結(jié)果竟然變了,而且每次都不一樣,所以本文就給大家講講如何解決sklearn中tsne算法降維結(jié)果不一致的問題2023-10-10關(guān)于Python參數(shù)解析器argparse的應(yīng)用場景
這篇文章主要介紹了關(guān)于Python參數(shù)解析器argparse的應(yīng)用場景,argparse 模塊使編寫用戶友好的命令行界面變得容易,程序定義了所需的參數(shù),而 argparse 將找出如何從 sys.argv 中解析這些參數(shù),需要的朋友可以參考下2023-08-08Python解決“argument?after?*?must?be?an?iterable”報錯問題
這篇文章主要介紹了Python解決“argument?after?*?must?be?an?iterable”報錯問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12Python中根據(jù)時間自動創(chuàng)建文件夾的代碼實現(xiàn)
這篇文章主要介紹了Python中根據(jù)時間自動創(chuàng)建文件夾的代碼實現(xiàn),這樣的話給工作帶來極大的便利,方便桌面文件按時間存放,具體實例代碼跟隨小編一起看看吧2021-10-10Python獲取網(wǎng)絡(luò)時間戳的兩種方法詳解
在我們進行注冊碼的有效期驗證時,通常使用獲取網(wǎng)絡(luò)時間的方式來進行比對。本文將介紹兩種利用Python獲取網(wǎng)絡(luò)時間戳的方法,感興趣的可以了解一下2022-01-01tensorflow將圖片保存為tfrecord和tfrecord的讀取方式
今天小編就為大家分享一篇tensorflow將圖片保存為tfrecord和tfrecord的讀取方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-02-02