python之面向對象編程一
概述:
- 面向過程:根據業務邏輯從上到下寫壘代碼
- 函數式:將某功能代碼封裝到函數中,日後便無需重復編寫,僅調用函數即可
- 面向對象:對函數進行分類和封裝,讓開發“更快更好更強...”
面向過程編程最易被初學者接受,其往往用一長段代碼來實現指定功能,開發過程中最常見的操作就是粘貼復制,即:將之前實現的代碼塊復制到現需功能處。隨著時間的推移,開始使用了函數式編程,增強代碼的重用性和可讀性。今天學習新的編程方式:面向對象編程(Object Oriented Programming,OOP,面向對象程序設計)
創建類和對象
面向對象編程是一種編程方式,此編程方式的落地需要使用 “類” 和 “對象” 來實現,所以,面向對象編程其實就是對 “類” 和 “對象” 的使用。
類就是一個模板,模板裏可以包含多個函數,函數裏實現一些功能。
對象則是根據模板創建的實例,通過實例對象可以執行類中的函數。
1 class people: 2 def wave(self): 3 print(‘Hello World!‘) 4 5 obj = people()
class為關鍵字,表示創建一個people的類 ,def wave為創建類中的函數,obj則是class創建的實例對象。
ps:類中的函數第一個參數必須是self, 類中定義的函數叫做 “方法”。
class people: def jon(self):print(‘say Hello!‘) def tom(self,name): print(‘say hello %s‘%name) obj = people() obj.jon() #執行jon方法 obj.tom(‘Jerry‘) #執行tom方法
通過上面代碼演示,你會認為使用函數式編程和面向對象編程方式來執行一個“方法”時函數要比面向對象簡便
- 面向對象:【創建對象】【通過對象執行方法】
- 函數編程:【執行函數】
觀察上述對比答案則是肯定的,然後並非絕對,場景的不同適合其的編程方式也不同。
總結:函數式的應用場景 --> 各個函數之間是獨立且無共用的數據。
面向對象的三大特性
面向對象的三大特性是指:封裝、繼承和多態。
一、封裝
封裝,顧名思義就是將內容封裝到某個地方,以後再去調用被封裝在某處的內容。
所以,在使用面向對象的封裝特性時,需要:
- 將內容封裝到某處
- 從某處調用被封裝的內容
第一步:將內容封裝到某處
# 創建類 class people: def __init__(self,name,age): self.name = name self.age = age #根據類people創建對象 #自動執行people類的__init__方法 p1 = people(‘xixi‘,21) p2 = people(‘hehe‘,23)
上面代碼__init__()稱為構造方法,根據類創建對象時自動執行
將xixi,21分別封裝到people的name和age屬性中
self是一個形式參數,當執行p1 = people(‘xixi‘,21)時,self等於p1
同理p2 = people(‘hehe‘,23)時,self等於p2
所以,內容實際被封裝到了對象,p1,p2中,每個對象都有name和age屬性,都封裝到對象p1,2中
第二步:從某處調用被封裝的內容
調用被封裝的內容時,有兩種情況:
- 通過對象直接調用
- 通過self間接調用
1、通過對象直接調用被封裝的內容
對象 p1 和 p2 在存儲到內存中,根據保存格式可以如此調用被封裝的內容:對象.屬性名
1 class people: 2 def __init__(self,name,age): 3 self.name = name 4 self.age = age 5 #根據類people創建對象 6 #自動執行people類的__init__方法 7 p1 = people(‘xixi‘,21) 8 print(p1.name) #直接調用p1對象的name屬性 9 print(p1.age) #p1的age屬性 10 p2 = people(‘hehe‘,23) 11 print(p2.name) 12 print(p2.age)View Code
2、通過self間接調用被封裝的內容
執行類中的方法時,需要通過self間接調用被封裝的內容:
1 class people: 2 def __init__(self,name,age): 3 self.name = name 4 self.age = age 5 6 def attr(self): 7 print(self.name) 8 print(self.age) 9 #根據類people創建對象 10 #自動執行people類的__init__方法 11 p1 = people(‘xixi‘,21) 12 p1.attr() 13 p2 = people(‘hehe‘,23) 14 p2.attr()View Code
Python默認會將p1傳給self參數,即:p1.attr(p1),所以,此時方法內部的 self = p1,即:self.name 是 xixi ;self.age 是 12,同理,p2也是如此.
綜上所述,對於面向對象的封裝來說,其實就是使用構造方法將內容封裝到 對象 中,然後通過對象直接或者self間接獲取被封裝的內容。
練習:在終端輸出如下信息
小明,10歲,男,上山去砍柴
小明,10歲,男,開車去東北
小明,10歲,男,最愛大保健
老李,90歲,男,上山去砍柴
老李,90歲,男,開車去東北
老李,90歲,男,最愛大保健
def kanchai(name, age, gender): print "%s,%s歲,%s,上山去砍柴" %(name, age, gender) def qudongbei(name, age, gender): print "%s,%s歲,%s,開車去東北" %(name, age, gender) def dabaojian(name, age, gender): print "%s,%s歲,%s,最愛大保健" %(name, age, gender) kanchai(‘小明‘, 10, ‘男‘) qudongbei(‘小明‘, 10, ‘男‘) dabaojian(‘小明‘, 10, ‘男‘) kanchai(‘老李‘, 90, ‘男‘) qudongbei(‘老李‘, 90, ‘男‘) dabaojian(‘老李‘, 90, ‘男‘)函數式編程
class Foo: def __init__(self, name, age ,gender): self.name = name self.age = age self.gender = gender def kanchai(self): print "%s,%s歲,%s,上山去砍柴" %(self.name, self.age, self.gender) def qudongbei(self): print "%s,%s歲,%s,開車去東北" %(self.name, self.age, self.gender) def dabaojian(self): print "%s,%s歲,%s,最愛大保健" %(self.name, self.age, self.gender) xiaoming = Foo(‘小明‘, 10, ‘男‘) xiaoming.kanchai() xiaoming.qudongbei() xiaoming.dabaojian() laoli = Foo(‘老李‘, 90, ‘男‘) laoli.kanchai() laoli.qudongbei() laoli.dabaojian()面向對象編程
二、繼承
繼承: 面向對象中的繼承和現實生活中的繼承相同,即:子可以繼承父的內容。
如:
男孩:talk,sleep,walk,eat
女孩:talk,sleep,walk,eat
class man: def talk(self): print ‘喵喵叫‘ def eat(self): # do something def walk(self): # do something def slepp(self): # do something class woman: def talk(self): print ‘喵喵叫‘ def eat(self): # do something def walk(self): # do something def slepp(self): # do something偽代碼
上述代碼不難看出,talk,sleep,walk,eat是男和女都具有的功能,而我們卻分別的男和nv的類中編寫了兩次。如果使用 繼承 的思想,如下實現:
people:talk,sleep,walk,eat
man:喝酒
woman:穿裙子
class people: def eat(self): # do something def walk(self): # do something def eat(self): # do something def sleep(self): # do something # 在類後面括號中寫入另外一個類名,表示當前類繼承另外一個類 class man(people): def 喝酒(self): print ‘喝酒‘ # 在類後面括號中寫入另外一個類名,表示當前類繼承另外一個類 class woman(people): def 穿裙子(self): print ‘穿裙子‘偽代碼
class people: def eat(self): print "%s 吃 " %self.name def walk(self): print "%s 走 " %self.name def sleep(self): print "%s 睡 " %self.name def talk(self): print "%s 說 " %self.name class man(people): def __init__(self, name): self.name = name def drink(self): print ‘喝酒‘ class woman(Animal): def __init__(self, name): self.name = name def wear(self): print ‘穿裙子‘ # ######### 執行 ######### p1 = man(‘yu‘) p1.eat() p2 = man(‘li‘) p2.drink() p3 = Dog(‘liu‘) p3.eat()實例演示
所以,對於面向對象的繼承來說,其實就是將多個類共有的方法提取到父類中,子類僅需繼承父類而不必一一實現每個方法。
註:除了子類和父類的稱謂,你可能看到過 派生類 和 基類 ,他們與子類和父類只是叫法不同而已。
那麽問題又來了,多繼承呢?
- 是否可以繼承多個類
- 如果繼承的多個類每個類中都定了相同的函數,那麽那一個會被使用呢?
1、Python的類可以繼承多個類,Java和C#中則只能繼承一個類
2、Python的類如果繼承了多個類,那麽其尋找方法的方式有兩種,分別是:深度優先和廣度優先
- 當類是經典類時,多繼承情況下,會按照深度優先方式查找
- 當類是新式類時,多繼承情況下,會按照廣度優先方式查找
經典類和新式類,從字面上可以看出一個老一個新,新的必然包含了跟多的功能,也是之後推薦的寫法,從寫法上區分的話,如果 當前類或者父類繼承了object類,那麽該類便是新式類,否則便是經典類。
經典類:
class p1: pass class p2(p1): pass
新式類:
class p1(object): pass class p2(p1): pass
class D: def bar(self): print ‘D.bar‘ class C(D): def bar(self): print ‘C.bar‘ class B(D): def bar(self): print ‘B.bar‘ class A(B, C): def bar(self): print ‘A.bar‘ a = A() # 執行bar方法時 # 首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中麽有,則繼續去D類中找,如果D類中麽有,則繼續去C類中找,如果還是未找到,則報錯 # 所以,查找順序:A --> B --> D --> C # 在上述查找bar方法的過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了 a.bar()經典類多繼承
class D(object): def bar(self): print ‘D.bar‘ class C(D): def bar(self): print ‘C.bar‘ class B(D): def bar(self): print ‘B.bar‘ class A(B, C): def bar(self): print ‘A.bar‘ a = A() # 執行bar方法時 # 首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中麽有,則繼續去C類中找,如果C類中麽有,則繼續去D類中找,如果還是未找到,則報錯 # 所以,查找順序:A --> B --> C --> D # 在上述查找bar方法的過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了 a.bar()新式類繼承
經典類:首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中麽有,則繼續去D類中找,如果D類中麽有,則繼續去C類中找,如果還是未找到,則報錯
新式類:首先去A類中查找,如果A類中沒有,則繼續去B類中找,如果B類中麽有,則繼續去C類中找,如果C類中麽有,則繼續去D類中找,如果還是未找到,則報錯
註意:在上述查找過程中,一旦找到,則尋找過程立即中斷,便不會再繼續找了
三、多態
Pyhon不支持Java和C#這一類強類型語言中多態的寫法,但是原生多態,其Python崇尚“鴨子類型”。
“當看到一只鳥走起來像鴨子、遊泳起來像鴨子、叫起來也像鴨子,那麽這只鳥就可以被稱為鴨子。”
我們並不關心對象是什麽類型,到底是不是鴨子,只關心行為。
鴨子類型在動態語言中經常使用,非常靈活,使得python不想java那樣專門去弄一大堆的設計模式。
class F1: pass class S1(F1): def show(self): print ‘S1.show‘ class S2(F1): def show(self): print ‘S2.show‘ def Func(obj): print obj.show() s1_obj = S1() Func(s1_obj) s2_obj = S2() Func(s2_obj)python‘鴨子類型‘
總結
以上就是本節對於面向對象初級知識的介紹,總結如下:
- 面向對象是一種編程方式,此編程方式的實現是基於對 類 和 對象 的使用
- 類 是一個模板,模板中包裝了多個“函數”供使用
- 對象,根據模板創建的實例(即:對象),實例用於調用被包裝在類中的函數
- 面向對象三大特性:封裝、繼承和多態.
類和對象在內存中是如何保存?
類以及類中的方法在內存中只有一份,而根據類創建的每一個對象都在內存中需要存一份,大致如下圖:
如上圖所示,根據類創建對象時,對象中除了封裝 name 和 age 的值之外,還會保存一個類對象指針,該值指向當前對象的類。
當通過p1執行 【方法一】 時,過程如下:
- 根據當前對象中的 類對象指針 找到類中的方法
- 將對象 p1當作參數傳給 方法的第一個參數 self
python之面向對象編程一