第二十六篇 面向對象初識
一. 三大編程範式
前面學完了Python的基本語法,能寫Python代碼, 而且可以處理工作中的一些問題,今天開始就要進入面向對象的學習了。首先,了解下三大編程範式,編程範式就是編程方法論,表明的是一種編程風格。
切記:三種編程風格沒有好壞之分,有分別的是使用不同風格的人。
1. 面向過程編程
核心是過程二字,過程指的是解決問題的步驟,即先幹什麽再幹什麽......面向過程的設計就好比精心設計好一條流水線,是一種機械式的思維方式。
優點是:復雜度的問題流程化,進而簡單化(一個復雜的問題,分成一個個小的步驟去實現,實現小的步驟將會非常簡單)
缺點是:一套流水線或者流程就是用來解決一個問題,生產汽水的流水線無法生產汽車,即便是能,也得是大改,改一個組件,牽一發而動全身。
應用場景:一旦完成基本很少改變的場景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
2. 函數式編程
包括數學層面的函數和代碼層面的函數。
3.面向對象編程
核心是對象二字,(要理解對象為何物,必須把自己當成上帝,上帝眼裏世間存在的萬物皆為對象,不存在的也可以創造出來。面向對象的程序設計好比如來設計西遊記,如來要解決的問題是把經書傳給東土大唐,如來想了想解決這個問題需要四個人:唐僧,沙和尚,豬八戒,孫悟空,每個人都有各自的特征和技能(這就是對象的概念,特征和技能分別對應對象的數據屬性和方法屬性),然而這並不好玩,於是如來又安排了一群妖魔鬼怪,為了防止師徒四人在取經路上被搞死,又安排了一群神仙保駕護航,這些都是對象。然後取經開始,師徒四人與妖魔鬼怪神仙交互著直到最後取得真經。如來根本不會管師徒四人按照什麽流程去取),對象是特征與技能的結合體,基於面向對象設計程序就好比在創造一個世界,你就是這個世界的上帝,存在的皆為對象,不存在的也可以創造出來,與面向過程機械式的思維方式形成鮮明對比,面向對象更加註重對現實世界的模擬,是一種“上帝式”的思維方式。
優點:
解決了程序的擴展性。對某一個對象單獨修改,會立刻反映到整個體系中,如對遊戲中一個人物參數的特征和技能修改都很容易。
缺點:
1. 編程的復雜度遠高於面向過程,不了解面向對象而立即上手基於它設計程序,極容易出現過度設計的問題。一些擴展性要求低的場景使用面向對象會徒增編程難度,比如管理linux系統的shell腳本就不適合用面向對象去設計,面向過程反而更加適合。
2. 無法向面向過程的程序設計流水線式的可以很精準的預測問題的處理流程與結果,面向對象的程序一旦開始就由對象之間的交互解決問題,即便是上帝也無法準確地預測最終結果。於是我們經常看到對戰類遊戲,新增一個遊戲人物,在對戰的過程中極容易出現陰霸的技能,一刀砍死3個人,這種情況是無法準確預知的,只有對象之間交互才能準確地知道最終的結果。
應用場景:需求經常變化的軟件,一般需求的變化都集中在用戶層,互聯網應用,企業內部軟件,遊戲等都是面向對象的程序設計大顯身手的好地方
面向對象的程序設計並不是全部。對於一個軟件質量來說,面向對象的程序設計只是用來解決擴展性。
二.編程進化論
1. 編程最開始就是無組織無結構,從簡單控制流中按步寫指令
2. 從上述的指令中提取重復的代碼塊或邏輯,組織到一起(比如,定義了一個函數),便實現了代碼重用,且代碼從無結構走向了結構化,創建程序的過程變得更具邏輯性。
3. 定義函數都是獨立於函數外定義變量,然後作為參數傳遞給函數,這意味著:數據與動作是分離的。
4. 如果把數據和動作內嵌到一個結構(函數或類)裏面,那麽就有了一個“對象矽統”(對象就是數據與函數整合到一起的產物)。
三. 顛覆一下面向對象
def dog(name,gender,type): # 狗的動作 def jiao(dog): print(‘一條狗[%s],汪汪汪‘ % dog[‘name‘]) def chi_shi(dog): print(‘一條[%s] 正在吃屎‘ % dog[‘type‘]) # 定義了init函數,作用是初始化狗,初始化了狗的特征和動作 def init(name,gender,type): dog1 = { # 初始化狗的特征 ‘name‘:name, ‘gender‘: gender, ‘type‘: type, # 初始化狗的動作 ‘jiao‘:jiao, ‘chi_shi‘:chi_shi, } return dog1 return init(name,gender,type) # d1,d2就是一個個具體的對象,是一條真真實實的狗 d1=dog(‘xiaohei‘,‘母‘,‘中華田園犬‘) d2=dog(‘heimei‘,‘母‘,‘藏敖‘) print(d1) print(d2) d1[‘jiao‘](d1) d2[‘chi_shi‘](d2) # 結果 {‘name‘: ‘xiaohei‘, ‘gender‘: ‘母‘, ‘type‘: ‘中華田園犬‘, ‘jiao‘: <function dog.<locals>.jiao at 0x00C11B28>, ‘chi_shi‘: <function dog.<locals>.chi_shi at 0x00C11AE0>} {‘name‘: ‘heimei‘, ‘gender‘: ‘母‘, ‘type‘: ‘藏敖‘, ‘jiao‘: <function dog.<locals>.jiao at 0x00C11A50>, ‘chi_shi‘: <function dog.<locals>.chi_shi at 0x00C11A98>} 一條狗[xiaohei],汪汪汪 一條[藏敖] 正在吃屎示例
# 如何描述一個對象? # 1.描述它的特征 # 2.描述它的動作 # 所以對象,就是特征與動作的結合 # 如何描述一個類? # 1. 描述他的特征 # 2. 描述他的動作 # 3. 把具有共性的特征和動作提取出來,抽象成一個類,比如人類, 植物等,就是一個類 # 對象就是從類裏抽象出的一個具體的個體, 類所具有的全部特征和動作,這個具體的對象都具有。 # 比如某個具體的人,你媳婦,她就具有人類所有的特征和動作。
為什麽說是顛覆一下面向對象呢?
因為常規認為面向對象都是class類,而此處卻用的是def函數,而且它的設計思想就是面向對象的思想。
三.類與對象
1. 什麽是類?
把一類事物的相同的特征和動作整合到一起就是類。類是一個抽象的概念。
2. 什麽是對象?
對象就是基於類而創建的一個具體的事物,具體真實的存在。對象也是特征和動作整合到一起的。
3. 那麽問題來了,先有的一個個具體存在的對象(比如一個具體存在的人),還是先有的人類這個概念,這個問題需要分兩種情況去看
(1)在現實世界中:先有對象,再有類
世界上肯定是先出現各種各樣的實際存在的物體,然後隨著人類文明的發展,人類站在不同的角度總結出了不同的種類,如人類、動物類、植物類等概念
也就說,對象是具體的存在,而類僅僅只是一個概念,並不真實存在
(2)在程序中:務必保證先定義類,後產生對象
這與函數的使用是類似的,先定義函數,後調用函數,類也是一樣的,在程序中需要先定義類,後調用類
不一樣的是,調用函數會執行函數體代碼返回的是函數體執行的結果,而調用類會產生對象,返回的是對象
好啦,下面看看怎麽一步步接近真相的。
# 需求 # 定義一個學校類 # 特征:name(學校名字), addr(學校地址), type(學校類型) # 動作:考試,招生,開除獎勵
1 # 原始方法 2 3 # 1. 特征 4 school1 = { 5 ‘name‘:‘人大‘, 6 ‘address‘:"海澱", 7 ‘type‘:‘985‘ 8 } 9 10 # 學校可以完成的動作 11 def kaoshi(school): 12 print("%s 學校正在考試" %school[‘name‘]) 13 14 def zhaosheng(shcool): 15 print("%s %s 學校正在招生" %(school(‘type‘),school[‘name‘]))原始方法
1 # 上面一個基本的學校類就定義好了,但是存在一個問題,就是可能來了一條狗也能調用考試的方法, 2 # 所以需要進化,把屬性和動作整合到一起,到目前為止通過作用域的方式可以完成 3 4 def school(): 5 # 1. 特征 6 school1 = { 7 ‘name‘: ‘人大‘, 8 ‘address‘: "海澱", 9 ‘type‘: ‘985‘ 10 } 11 12 # 學校可以完成的動作 13 def kaoshi(school): 14 print("%s 學校正在考試" % school[‘name‘]) 15 16 def zhaosheng(shcool): 17 print("%s %s 學校正在招生" % (school(‘type‘), school[‘name‘]))第一次進化
1 # 這裏雖然完成了把屬性和動作的整合,保證外部來的那條狗是無法調用了學校了,但是,又出現個新問題, 2 # 就是寫死了一個對象school1,有沒有什麽方法可以實現多個對象呢? 3 4 def school(name,addr,typer): 5 # 1. 特征 6 school1 = { 7 ‘name‘: name, # 這裏就不能寫死了,需要一個變量替代,變量從外部傳入 8 ‘address‘: addr, 9 ‘type‘: typer 10 } 11 12 # 學校可以完成的動作 13 def kaoshi(school): 14 print("%s 學校正在考試" % school[‘name‘]) 15 16 def zhaosheng(shcool): 17 print("%s %s 學校正在招生" % (school(‘type‘), school[‘name‘]))第二次進化
1 # 這裏雖然完成了把屬性和動作的整合,保證外部來的那條狗是無法調用了學校了,但是,又出現個新問題, 2 # 就是寫死了一個對象school1,有沒有什麽方法可以實現多個對象呢? 3 4 def school(name,addr,typer): 5 # 1. 特征 6 school1 = { 7 ‘name‘: name, # 這裏就不能寫死了,需要一個變量替代,變量從外部傳入 8 ‘address‘: addr, 9 ‘type‘: typer 10 } 11 12 # 學校可以完成的動作 13 def kaoshi(school): 14 print("%s 學校正在考試" % school[‘name‘]) 15 16 def zhaosheng(shcool): 17 print("%s %s 學校正在招生" % (school(‘type‘), school[‘name‘]))第三次進化
1 1 def school(name,addr,typer): 2 2 3 3 # 學校可以完成的動作 4 4 def kaoshi(school): 5 5 print("%s 學校正在考試" % school[‘name‘]) 6 6 7 7 def zhaosheng(school): 8 8 print("%s %s 學校正在招生" % (school[‘type‘], school[‘name‘])) 9 9 10 10 # 1. 特征 11 11 sch = { 12 12 ‘name‘: name, # 這裏就不能寫死了,需要一個變量替代,變量從外部傳入 13 13 ‘address‘: addr, 14 14 ‘type‘: typer, 15 15 # 在特征裏實現對象和動作的綁定 16 16 ‘kao_shi‘:kaoshi, 17 17 ‘zhao_sheng‘:zhaosheng 18 18 } 19 19 # 對象生成了,需要返回才可以 20 20 return sch 21 21 22 22 # 生成對象s1人大 23 23 s1 = school(‘人大‘,‘海澱‘,‘985‘) 24 24 print(s1) 25 25 # 返回的是字典 26 26 # {‘name‘: ‘人大‘, ‘address‘: ‘海澱‘, ‘type‘: ‘985‘, ‘kao_shi‘: <function school.<locals>.kaoshi at 0x007E1AE0>, ‘zhao_sheng‘: <function school.<locals>.zhaosheng at 0x007E1A50>} 27 27 28 28 # 可以通過生成的對象來調學校的名字了 29 29 print(s1[‘name‘]) 30 30 # 人大 31 31 32 32 # 可以看這個學校有什麽動作 33 33 print(s1[‘kao_shi‘]) # <function school.<locals>.kaoshi at 0x003F1AE0> 34 34 35 35 print(s1[‘zhao_sheng‘]) # <function school.<locals>.zhaosheng at 0x003F1A50> 36 36 37 37 38 38 # 如果想讓新生成的s1人大這個對象來執行學校的考試動作 39 39 s1[‘kao_shi‘](s1) 40 40 # 人大 學校正在考試 41 41 42 42 # 如果想讓新生成的s1人大這個對象來執行學校的招生動作 43 43 s1[‘zhao_sheng‘](s1) 44 44 # 985 人大 學校正在招生第四次進化
‘‘‘ 上面的程序已經可以完成功能了,但是還是不夠好, ‘‘‘ def school(name,addr,typer): # 讓他變的更好,要在局部作用域裏,定義一個init的函數 def init(name,addr,typer): # 1. 特征 sch = { ‘name‘: name, # 這裏就不能寫死了,需要一個變量替代,變量從外部傳入 ‘address‘: addr, ‘type‘: typer, # 在特征裏實現對象和動作的綁定 ‘kao_shi‘: kaoshi, ‘zhao_sheng‘: zhaosheng } # 對象生成了,需要返回才可以 return sch # 學校可以完成的動作 def kaoshi(school): print("%s 學校正在考試" % school[‘name‘]) def zhaosheng(school): print("%s %s 學校正在招生" % (school[‘type‘], school[‘name‘])) # 先 看下結構,代碼就變得整潔了第五次進化
# 但是,又有 一個問題,執行school函數,函數體內沒有任何運行,而我們的目的是一執行school函數,立馬回返回一個真實存在的s1的對象, # 那怎麽才能生成這個對象呢?而做這件事的就是init函數,所以只需要執行school函數的時候,運行init函數就行了唄。所以加上init(),另外, # 函數運行,需要返回結果哦,所以是 return init() def school(name,addr,typer): # 讓他變的更好,要在局部作用域裏,定義一個init的函數 def init(name,addr,typer): # 1. 特征 sch = { ‘name‘: name, # 這裏就不能寫死了,需要一個變量替代,變量從外部傳入 ‘address‘: addr, ‘type‘: typer, # 在特征裏實現對象和動作的綁定 ‘kao_shi‘: kaoshi, ‘zhao_sheng‘: zhaosheng } # 對象生成了,需要返回才可以 return sch # 學校可以完成的動作 def kaoshi(school): print("%s 學校正在考試" % school[‘name‘]) def zhaosheng(school): print("%s %s 學校正在招生" % (school[‘type‘], school[‘name‘])) # 運行init()函數,並返回運行結果 return init(name,addr,typer) s1 = school(‘清華‘,‘五道口‘,‘公立‘) print(s1) s2 = school(‘人大‘,‘魏公村‘,‘公立‘) print(s2) s2[‘zhao_sheng‘](s2)第六次進化
上面的實現過程就是一個面向對象的設計過程思想
四. 面向對象設計(Object oriented design)
所謂面向對象設計:就是將一類事物的具體數據和動作整合到一起,即面向對象設計
面向對象設計(OOD)不會特別要求面向對象編程語言。事實上,OOD可以由純結構化語言來實現(比如C)。但如果想要構造局對對象性質和特點的數據類型,就需要在程序上作更多努力。
五. 面向對象編程(object-oriented programming)
所謂面向對象編程:就是定義類+實例/對象的方法去實現面向對象的設計
# 用面向對象編程獨有的語法class去實現面向對象設計 # 最直觀的展示面向對象展示
‘‘‘ 這段程序裏有兩個地方要註意:第一個是class,第二個是方法後面的self,這個self到底是什麽東東?
‘‘‘
關於self,其實說白了就是對象自己。
對於函數來說,就是一個位置參數,函數在運行的時間,需要把自己傳進來。
關於self,其實說白了就是對象自己。 對於函數來說,就是一個位置參數,函數在運行的時間,需要把自己傳進來。 用上面的示例可以清楚的看到 def school(name,addr,typer): def init(name,addr,typer): sch = { ‘name‘: name, ‘address‘: addr, ‘type‘: typer, ‘kao_shi‘: kaoshi, ‘zhao_sheng‘: zhaosheng } return sch def kaoshi(school): print("%s 學校正在考試" % school[‘name‘]) def zhaosheng(school): print("%s %s 學校正在招生" % (school[‘type‘], school[‘name‘])) # 替換成self後,原理就是這樣。 def school(name,addr,typer): def init(name,addr,typer): sch = { ‘name‘: name, ‘address‘: addr, ‘type‘: typer, ‘kao_shi‘: kaoshi, ‘zhao_sheng‘: zhaosheng } return sch def kaoshi(self): print("%s 學校正在考試" % self[‘name‘]) def zhaosheng(self): print("%s %s 學校正在招生" % (self[‘type‘], self[‘name‘]))關於self
小總結
1. 有人說,只有class定義類才是面向對象,def定義函數就是函數相關的,跟面向對象沒關系----錯的哦,上面已經很清楚的通過def展示了面向對象。
2. 一門面向對象語言不一定會強制你寫面向對象(OO)方面的程序。例如,C++可以被認為“更好的C”;而java則要求萬物皆類,此外,還規定,一個源文件對應一個類定義。
3.然而,在Python中,類和面向對象編程OOP都不是日常編程所必須的。盡管它從一開始設計就是面向對象的,並且結構上支持OOP,但Python沒有限定或要求你在你的應用中寫OO的代碼。
Python是面向對象語言,提供了豐富的class方面的內容。
4. 用面向對象語言寫程序,和一個程序的設計是面向對象的,兩者是八竿子打不著的兩碼事。
第二十六篇 面向對象初識