面向對象的詳細解讀 一、基本概念 1. 面向過程 (1) 概念:以過程為中心的編程思想,就是分析出解決問題所需要的步驟,然後用函數把這些步驟一步一步實現,使用的時候一個一個依次調用就可以了。 (2) 優點:極大的降低了寫程式的複雜度,只需要順著要執行的步驟,堆疊代碼即可。因為程式的流程很清楚,按著模 ...
面向對象的詳細解讀
一、基本概念
1. 面向過程
(1) 概念:以過程為中心的編程思想,就是分析出解決問題所需要的步驟,然後用函數把這些步驟一步一步實現,使用的時候一個一個依次調用就可以了。
(2) 優點:極大的降低了寫程式的複雜度,只需要順著要執行的步驟,堆疊代碼即可。因為程式的流程很清楚,按著模塊與函數的方法可以很好的組織。
(3) 缺點:只能用來解決一個問題,代碼牽一發而動全身。
(4) 應用:用於基本很少改變的場景,著名的例子有Linux內核、git、以及Apache HTTP Server等。
2. 面向對象
(1) 概念:程式設計的核心是對象,面向對象的方法主要是把事物給對象化,對象包括屬性與方法。
(2) 優點:易維護、易復用、易擴展,由於面向對象有封裝、繼承、多態性的特性,可以設計出低耦合的系統,使系統更加靈活、更加易於維護。
(3) 缺點:可控性差,無法像面向過程的程式設計一樣可以精準的預測問題的處理流程與結果。
(4) 應用:需求經常變化的軟體,一般需求的變化都集中在用戶層,如互聯網應用、企業內部軟體以及游戲等。
二、面向對象中的名詞解析
1. 類:類就是一個模板,模板里可以包含多個函數,每個函數都能實現一些功能。
2. 對象:根據模板(類)創建的實例,通過這個實例(對象)可以執行類中的函數。
3. 屬性:類中所定義的變數
4. 方法:類中所定義的函數
5. 實例化:通過類創建對象的過程
6. 總結:對象的抽象是類,類的具體化就是對象;也可以說類的實例化是對象,對象是類的實例。
》》》實例說明——類與對象的創建
# -*- encoding:utf-8 -*- ''' 實例說明——類與對象的創建 ''' class ClassName: #創建類,ClassName為類名 Alary = 10000 #類的一個屬性 def Fun1(self): #方法1 pass #方法1的技能 def Fun2(self): #方法2 pass #方法2的技能 obj = ClassName() #創建對象,obj為對象名
三、特征
1. 封裝
(1) 概念:封裝是將對象運行時所需的資源封裝在程式對象中。簡單來說,就是將內容封裝起來,以後再去調用被封裝的內容。
(2) 調用封裝的內容有2種方法:
——通過對象直接調用
——通過self間接調用
》》》實例說明——面向對象的特征:封裝
# -*- encoding:utf-8 -*- ''' 實例說明——面向對象的特征:封裝 ''' class Student: def __init__(self, name, age): self.name = name self.age = age def detail(self): #調用時對象名會傳給self參數,如最後一句 print(self.name) print(self.age) obj1 = Student('Jack',15) #將'Jack'和15分別封裝到obj1的self的name和age屬性中 print(obj1.name) #通過對象直接調用name屬性和age屬性 print(obj1.age) obj2 = Student('Sahra',13) #將'Sahra'和13分別封裝到obj2的self的name和age屬性中 obj2.detail() #通過self間接調用name屬性和age屬性
2. 繼承
(1) 概念:繼承可以使得子類別具有父類別的各種屬性和方法,而不需要再次編寫相同的代碼。在令子類別繼承父類別的同時,可以重新定義某些屬性,並重寫某些方法,即覆蓋父類別的原有屬性和方法,使其獲得與父類別不同的功能。
(2) 多繼承
註:Python的類可以繼承多個類,而Java和C#中則只能繼承一個類
Python的類如果繼承了多個類,那麼其尋找方法的方式有2種:
- 當類為經典類時會按照深度優先方式查找
- 當類為新式類時會按照廣度優先方式查找
》》》實例說明——面向對象的特征:繼承
# -*- encoding:utf-8 -*- ''' 實例說明——面向對象的特征:繼承 ''' class Person(object): #定義Person父類 def talk(self): #定義父類的方法 print("Person can talk.") class Chinese(Person): #定義Person父類的一個子類,同時是Characters類的父類 def talkC(Person): #定義方法 print("Chinese can talk Mandarin.") class Characters(Chinese): #定義Chinese父類的一個子類 def people(self): #定義方法 print("Chinese are clever and diligent.") class American(Person): #定義Person父類的一個子類 def talkA(self): #定義方法 print("American can talk English.") C = Characters() #定義父類的子類的子類 A = American() #定義父類的子類 C.talk() #調用繼承Person類的方法 A.talkA() #調用本身的方法 C.people() #調用本身的方法
3. 多態
概念:多態指同一個實體同時具有多種形式,在賦值之後,不同的子類對象調用相同的父類方法,產生的執行結果不同。
》》》實例說明——面向對象的特征:多態
1 # -*- encoding:utf-8 -*- 2 ''' 實例說明——面向對象的特征:多態 ''' 3 import abc 4 5 class Animal(metaclass=abc.ABCMeta): #同一類事物:動物 6 7 @abc.abstractmethod 8 def talk(self): 9 pass 10 11 class People(Animal): #動物的形態之一:人 12 def talk(self): 13 print('say hello') 14 15 class Dog(Animal): #動物的形態之二:狗 16 def talk(self): 17 print('say wangwang') 18 19 class Pig(Animal): #動物的形態之三:豬 20 def talk(self): 21 print('say aoao') 22 23 24 peo = People() #創建People類的對象peo 25 dog = Dog() #創建Dog類的對象dog 26 pig = Pig() #創建Pig類的對象pig 27 peo.talk() #分別使用各種的方法 28 dog.talk() 29 pig.talk()
四、類的成員
1. 簡介:類的成員包括欄位、方法和屬性。在所有成員中,只有欄位中的普通欄位保存於對象中,因此創建多少個對象在記憶體中就有幾個普通欄位;而其他成員都保存在類中,也只占用一份記憶體空間。
2. 欄位:包括普通欄位和靜態欄位,他們在定義和使用都有所區別,而最本質的區別是記憶體中保存的位置不同。
(1) 普通欄位保存在對象中
(2) 靜態欄位保存在類中
3. 方法:包括普通方法、靜態方法和類方法,三種方法在記憶體中都歸屬於類,區別在於調用方式不同。
(1) 公有方法
調用:可由對象名直接調用;如果通過類名來調用屬於對象的公有方法,需要顯式為該方法的self參數傳遞一個對象名,用來明確指定訪問哪個對象的數據成員。
特點:至少一個self參數;執行普通方法時,自動將調用該方法的對象賦值給self。在此方法中可以訪問屬於類和對象的成員。
(2) 私有方法
調用:只能在屬於對象的方法中通過self調用或在外部通過Python支持的特殊方式來調用。在此方法中可以訪問屬於類和對象的成員。
(3) 靜態方法
調用:可以通過類名和對象名調用,但不能直接訪問屬於對象的成員,只能訪問屬於類的成員。
特點:無預設參數。
(4) 類方法
調用:可以通過類名和對象名調用,但不能直接訪問屬於對象的成員,只能訪問屬於類的成員。
特點:至少一個cls參數;執行類方法時,自動將調用該方法的類複製給cls。
4. 屬性:其實是普通方法的變種。
5. 類成員的修飾符:下劃線
xxx :公有成員,在任何地方都能訪問
__xxx or ...__xxx:私有成員,只有類對象自己能訪問,子類對象不能直接訪問,但在對象外部可以通過“對象名._類名__xxx”這樣的特殊方式來訪問。
_xxx:受保護成員,不能用'from module import *'導入
__xxx__:系統定義的特殊成員
註:Python中不存在嚴格意義上的私有成員
6. 類的特殊成員
(1) __init__:構造方法,通過類創建對象時,自動觸發執行。
(2) __del__:析構方法,當對象在記憶體中被釋放時,自動觸發執行,此方法一般無需定義。
(3) 類的屬性
dir(類名):查出的是一個名字列表
類名.__dict__:查出的是一個字典,key為屬性名,value為屬性值
(4) 特殊的類屬性
類名.__name__:類的名字(字元串)
類名.__doc__:類的文檔字元串
類名.__base__:類的第一個父類(在講繼承時會講)
類名.__bases__:類所有父類構成的元組(在講繼承時會講)
類名.__dict__:類的字典屬性
類名.__module__:類定義所在的模塊
類名.__class__:實例對應的類(僅新式類中)
7. 混入機制
(1) 概念:Python類型的動態性使得我們可以動態為自定義類及其對象增加新的屬性和行為,俗稱混入(mixin)機制。
(2) 》》》實例說明 —— 混入機制
1 # -*- encoding:utf-8 -*- 2 ''' 實例說明 —— 混入機制 ''' 3 import types 4 class Car: #定義類 5 price = 100000 #定義類屬性,屬於靜態欄位,只保存於類中 6 def __init__(self, c): 7 self.color = c #定義實例屬性 8 9 mycar = Car("Blue") #實例化對象 10 print(mycar.color, Car.price) #查看實例屬性和類屬性的值 11 Car.price = 1200 #修改類屬性的值 12 Car.name = 'Roman' #動態增加類屬性,屬於靜態欄位,只保存於類中 13 mycar.color = "Red" #修改實例屬性的值 14 print(Car.price, Car.name, mycar.color) 15 16 def setSpeed(self, s): 17 self.speed = s 18 19 mycar.setSpeed = types.MethodType(setSpeed, mycar) #動態增加成員方法 20 mycar.setSpeed(50) #調用成員方法 21 print(mycar.speed) 22 23 ''' 利用特殊的類屬性查看類與對象的屬性與行為 ''' 24 print("Car類的屬性與行為如下:\n",Car.__dict__) 25 print("mycar對象的屬性與行為如下:\n",mycar.__dict__)
五、使用面向對象思想實現基本操作
1. 三維向量類
(1) 簡述:實現向量的加減法、向量與標量的乘除法。
(2) 代碼實現:
# --coding: gb2312-- ''' 三維向量 ''' class vector3: def __init__(self, x_ = 0, y_ = 0, z_ = 0): #構造函數 self.x = x_ self.y = y_ self.z = z_ def __add__(self, obj): #重載+作為加號 return vector3(self.x+obj.x, self.y+obj.y, self.z+obj.z) def __sub__(self, obj): #重載-作為減號 return vector3(self.x-obj.x, self.y-obj.y, self.z-obj.z) def __mul__(self,n): #重載*作為點乘 return vector3(self.x*n, self.y*n, self.z*n) def __truediv__(self, obj): #重載/作為除法 return vector3(self.x/n, self.y/n, self.z/n) def __str__(self): return str(self.x)+','+str(self.y)+','+str(self.z) if __name__ == "__main__": n = int(input("請輸入一個標量:")) a,b,c = map(int,input("請輸入第一個向量:").split()) v1 = vector3(a,b,c) a,b,c = map(int,input("請輸入第二個向量:").split()) v2 = vector3(a,b,c) print("兩向量的加法:",v1 + v2) print("兩向量的減法:",v1 - v2) print("標量與向量的乘法:",v1 * n) print("標量與向量的除法:",v1 / n)
2. 英文字元串處理
(1) 簡述:用戶輸入一段英文,得到這段英文中所以長度為3的單詞,並去除重覆的單詞。
(2) 代碼實現:
方法一: 使用 jieba 庫
1 # -*- encoding:utf-8 -*- 2 ''' 將一段英文中長度為3的單詞輸出,並去掉重覆的單詞 ''' 3 4 import jieba 5 class ProString: 6 Str = "" 7 Dict = {} 8 Ls = [] 9 def __init__(self,string,length = 3): #初始化 10 self.string = string 11 self.length = length 12 13 def SignalWord(self): #去除重覆的單詞 14 self.words = jieba.lcut(self.string) #jieba分詞 15 for _ in self.words: #與詞頻演算法相似 16 self.Dict[_] = self.Dict.get(_,0) + 1 17 del(self.Dict[' ']) #刪除空格項 18 self.Ls = list(self.Dict.keys()) #字典類型轉化成列表類型 19 self.StubbenWord(self.Ls) 20 21 def StubbenWord(self,Ls): #利用去除重覆的單詞,得到固定長度的單詞 22 for _ in Ls: 23 if len(_) == self.length: 24 self.Str += _ + ' ' 25 self.printf(self.Str) 26 27 def printf(self,Str): 28 print("處理後的字元串為:",Str) 29 30 if __name__ == "__main__": 31 str = input("請輸入字元串:") 32 process = ProString(str,3) 33 process.SignalWord()
方法二: 使用 re庫 (正則表達式)
1 # -*- encoding:utf-8 -*- 2 ''' 將一段英文中長度為3的單詞輸出,並去掉重覆的單詞 ''' 3 4 import re 5 class ProStr: 6 a = [] 7 def __init__(self, words, length = 3): 8 self.words = words 9 self.length = length 10 11 def process(self): 12 word_list = re.split('[\. ]+',self.words) 13 for _ in word_list: 14 if len(_) == self.length: 15 if _ not in self.a: 16 self.a.append(_) 17 else: 18 continue 19 self.printf() 20 21 def printf(self): 22 print("處理後的字元串為:", end = '') 23 for _ in range(len(self.a)): 24 print(self.a[_],end=' ') 25 26 if __name__ == "__main__": 27 words = input("請輸入字元串:") 28 process = ProStr(words, 3) 29 process.process()
<拓展閱讀>
1. 定義類方法時一定要有self 參數嗎?
答:在Python中,定義類的方法時將第一個參數定義為“self”只是一個習慣,而實際上類的方法中第一個參數的名字是可以變化的,而不一定要使用“self”這個名字,但一般建議編寫代碼時仍以“self”作為方法的第一個參數名字。
2. 在python中,方法與函數有區別嗎?
答:在Python中,函數和方法是有區別的。方法一般指與特定實例綁定的函數,通過對象調用方法時,對象本身將被作為第一個參數隱式傳遞過去,普通函數並不具備這個特點。
3. 類和對象在記憶體中如何保存?
答:類以及類中的方法在記憶體中只占一份記憶體,而根據類創建的每一個對象在記憶體中都需要一份記憶體。如下圖:
如上圖所示,根據類創建對象時,對象中除了封裝 name 和 age 的值之外,還會保存一個類對象指針,該值指向當前對象的類。
當通過 obj1 執行 【方法一】 時,過程如下:
- 根據當前對象中的 類對象指針 找到類中的方法
- 將對象 obj1 當作參數傳給 方法的第一個參數 self
4. IDLE中下劃線’_’的奇葩用法
(1) 表示解釋器中最後一次顯示的內容或最後一個語句正確執行的輸出結果
(2) 表示不關心的變數的值
(3) 在對象、類名或模塊後面加上一個圓點 '.',稍等一秒鐘則會自動列出其所有公開成員。如果在圓點 '.' 後面再加一個下劃線 '_',則會列出其所有成員,包括私有成員。