Python 面向对象(初级篇)
概述
- 面向過程:根據(jù)業(yè)務(wù)邏輯從上到下寫壘代碼
- 函數(shù)式:將某功能代碼封裝到函數(shù)中,日后便無需重復(fù)編寫,僅調(diào)用函數(shù)即可
- 面向?qū)ο?#xff1a;對(duì)函數(shù)進(jìn)行分類和封裝,讓開發(fā)“更快更好更強(qiáng)...”
面向過程編程最易被初學(xué)者接受,其往往用一長(zhǎng)段代碼來實(shí)現(xiàn)指定功能,開發(fā)過程中最常見的操作就是粘貼復(fù)制,即:將之前實(shí)現(xiàn)的代碼塊復(fù)制到現(xiàn)需功能處。
while True: if cpu利用率 > 90%: #發(fā)送郵件提醒 連接郵箱服務(wù)器 發(fā)送郵件 關(guān)閉連接 if 硬盤使用空間 > 90%: #發(fā)送郵件提醒 連接郵箱服務(wù)器 發(fā)送郵件 關(guān)閉連接 if 內(nèi)存占用 > 80%: #發(fā)送郵件提醒 連接郵箱服務(wù)器 發(fā)送郵件 關(guān)閉連接隨著時(shí)間的推移,開始使用了函數(shù)式編程,增強(qiáng)代碼的重用性和可讀性,就變成了這樣:
def 發(fā)送郵件(內(nèi)容) #發(fā)送郵件提醒 連接郵箱服務(wù)器 發(fā)送郵件 關(guān)閉連接 while True: if cpu利用率 > 90%: 發(fā)送郵件('CPU報(bào)警') if 硬盤使用空間 > 90%: 發(fā)送郵件('硬盤報(bào)警') if 內(nèi)存占用 > 80%: 發(fā)送郵件('內(nèi)存報(bào)警')今天我們來學(xué)習(xí)一種新的編程方式:面向?qū)ο缶幊?#xff08;Object Oriented Programming,OOP,面向?qū)ο蟪绦蛟O(shè)計(jì))
注:?Java和C#來說只支持面向?qū)ο缶幊?#xff0c;而python比較靈活即支持面向?qū)ο缶幊桃仓С趾瘮?shù)式編程
創(chuàng)建類和對(duì)象面向?qū)ο缶幊淌且环N編程方式,此編程方式的落地需要使用 “類” 和 “對(duì)象” 來實(shí)現(xiàn),所以,面向?qū)ο缶幊唐鋵?shí)就是對(duì) “類” 和 “對(duì)象” 的使用。
- 類就是一個(gè)模板,模板里可以包含多個(gè)函數(shù),函數(shù)里實(shí)現(xiàn)一些功能
- 對(duì)象則是根據(jù)模板創(chuàng)建的實(shí)例,通過實(shí)例對(duì)象可以執(zhí)行類中的函數(shù)
- class是關(guān)鍵字,表示類
- 創(chuàng)建對(duì)象,類名稱后加括號(hào)即可
ps:類中的函數(shù)第一個(gè)參數(shù)必須是self(詳細(xì)見:類的三大特性之封裝)
類中定義的函數(shù)叫做 “方法”
誒,你在這里是不是有疑問了?使用函數(shù)式編程和面向?qū)ο缶幊谭绞絹韴?zhí)行一個(gè)“方法”時(shí)函數(shù)要比面向?qū)ο蠛?jiǎn)便
- 面向?qū)ο?#xff1a;【創(chuàng)建對(duì)象】【通過對(duì)象執(zhí)行方法】
- 函數(shù)編程:【執(zhí)行函數(shù)】
觀察上述對(duì)比答案則是肯定的,然后并非絕對(duì),場(chǎng)景的不同適合其的編程方式也不同。
總結(jié):函數(shù)式的應(yīng)用場(chǎng)景 --> 各個(gè)函數(shù)之間是獨(dú)立且無共用的數(shù)據(jù)
面向?qū)ο笕筇匦?/span>面向?qū)ο蟮娜筇匦允侵?#xff1a;封裝、繼承和多態(tài)。
一、封裝
封裝,顧名思義就是將內(nèi)容封裝到某個(gè)地方,以后再去調(diào)用被封裝在某處的內(nèi)容。所以,在使用面向?qū)ο蟮姆庋b特性時(shí),需要:
- 將內(nèi)容封裝到某處
- 從某處調(diào)用被封裝的內(nèi)容
第一步:將內(nèi)容封裝到某處
self 是一個(gè)形式參數(shù),當(dāng)執(zhí)行?obj1 = Foo('wupeiqi', 18 )?時(shí),self?等于?obj1,當(dāng)執(zhí)行?obj2 = Foo('alex', 78 )?時(shí),self?等于?obj2
所以,內(nèi)容其實(shí)被封裝到了對(duì)象?obj1?和?obj2?中,每個(gè)對(duì)象中都有?name?和?age屬性,在內(nèi)存里類似于下圖來保存。
第二步:從某處調(diào)用被封裝的內(nèi)容
調(diào)用被封裝的內(nèi)容時(shí),有兩種情況:
- 通過對(duì)象直接調(diào)用
- 通過self間接調(diào)用
1、通過對(duì)象直接調(diào)用被封裝的內(nèi)容
上圖展示了對(duì)象 obj1 和 obj2 在內(nèi)存中保存的方式,根據(jù)保存格式可以如此調(diào)用被封裝的內(nèi)容:對(duì)象.屬性名
class Foo: def __init__(self, name, age): self.name = name self.age = age obj1 = Foo('wupeiqi', 18) print obj1.name # 直接調(diào)用obj1對(duì)象的name屬性 print obj1.age # 直接調(diào)用obj1對(duì)象的age屬性 obj2 = Foo('alex', 73) print obj2.name # 直接調(diào)用obj2對(duì)象的name屬性 print obj2.age # 直接調(diào)用obj2對(duì)象的age屬性2、通過self間接調(diào)用被封裝的內(nèi)容
執(zhí)行類中的方法時(shí),需要通過self間接調(diào)用被封裝的內(nèi)容
class Foo: def __init__(self, name, age): self.name = name self.age = age def detail(self): print self.name print self.age obj1 = Foo('wupeiqi', 18) obj1.detail() # Python默認(rèn)會(huì)將obj1傳給self參數(shù),即:obj1.detail(obj1),所以,此時(shí)方法內(nèi)部的 self = obj1,即:self.name 是 wupeiqi ;self.age 是 18 obj2 = Foo('alex', 73) obj2.detail() # Python默認(rèn)會(huì)將obj2傳給self參數(shù),即:obj1.detail(obj2),所以,此時(shí)方法內(nèi)部的 self = obj2,即:self.name 是 alex ; self.age 是 78綜上所述,對(duì)于面向?qū)ο蟮姆庋b來說,其實(shí)就是使用構(gòu)造方法將內(nèi)容封裝到對(duì)象中,然后通過對(duì)象直接或者self間接獲取被封裝的內(nèi)容。
練習(xí)一:在終端輸出如下信息
小明,10歲,男,上山去砍柴
小明,10歲,男,開車去東北
小明,10歲,男,最愛大保健
老李,90歲,男,上山去砍柴
老李,90歲,男,開車去東北
老李,90歲,男,最愛大保健
老張...
上述對(duì)比可以看出,如果使用函數(shù)式編程,需要在每次執(zhí)行函數(shù)時(shí)傳入相同的參數(shù),如果參數(shù)多的話,又需要粘貼復(fù)制了... ;而對(duì)于面向?qū)ο笾恍枰趧?chuàng)建對(duì)象時(shí),將所有需要的參數(shù)封裝到當(dāng)前對(duì)象中,之后再次使用時(shí),通過self間接去當(dāng)前對(duì)象中取值即可。
練習(xí)二:游戲人生程序
1、創(chuàng)建三個(gè)游戲人物,分別是:
蒼井井,女,18,初始戰(zhàn)斗力1000
東尼木木,男,20,初始戰(zhàn)斗力1800
波多多,女,19,初始戰(zhàn)斗力2500
2、游戲場(chǎng)景,分別:
草叢戰(zhàn)斗,消耗200戰(zhàn)斗力
自我修煉,增長(zhǎng)100戰(zhàn)斗力
多人游戲,消耗500戰(zhàn)斗力
二、繼承
繼承,面向?qū)ο笾械睦^承和現(xiàn)實(shí)生活中的繼承相同,即:子可以繼承父的內(nèi)容。
例如:
貓可以:喵喵叫、吃、喝、拉、撒
狗可以:汪汪叫、吃、喝、拉、撒
如果我們要分別為貓和狗創(chuàng)建一個(gè)類,那么就需要為貓和狗實(shí)現(xiàn)他們所有的功能,如下所示:
class 貓: def 喵喵叫(self): print '喵喵叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something class 狗: def 汪汪叫(self): print '汪汪叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something 偽代碼上述代碼不難看出,吃、喝、拉、撒是貓和狗都具有的功能,而我們卻分別的貓和狗的類中編寫了兩次。如果使用 繼承 的思想,如下實(shí)現(xiàn):
動(dòng)物:吃、喝、拉、撒
貓:喵喵叫(貓繼承動(dòng)物的功能)
狗:汪汪叫(狗繼承動(dòng)物的功能)
所以,對(duì)于面向?qū)ο蟮睦^承來說,其實(shí)就是將多個(gè)類共有的方法提取到父類中,子類僅需繼承父類而不必一一實(shí)現(xiàn)每個(gè)方法。
注:除了子類和父類的稱謂,你可能看到過派生類和基類 ,他們與子類和父類只是叫法不同而已。
學(xué)習(xí)了繼承的寫法之后,我們用代碼來是上述阿貓阿狗的功能:
class Animal: def eat(self): print "%s 吃 " %self.name def drink(self): print "%s 喝 " %self.name def shit(self): print "%s 拉 " %self.name def pee(self): print "%s 撒 " %self.name class Cat(Animal): def __init__(self, name): self.name = name self.breed = '貓' def cry(self): print '喵喵叫' class Dog(Animal): def __init__(self, name): self.name = name self.breed = '狗' def cry(self): print '汪汪叫' ######### 執(zhí)行 ######### c1 = Cat('小白家的小黑貓') c1.eat() c2 = Cat('小黑的小白貓') c2.drink() d1 = Dog('胖子家的小瘦狗') d1.eat() 代碼實(shí)例那么問題又來了,多繼承呢?
- 是否可以繼承多個(gè)類
- 如果繼承的多個(gè)類每個(gè)類中都定了相同的函數(shù),那么那一個(gè)會(huì)被使用呢?
1、Python的類可以繼承多個(gè)類,Java和C#中則只能繼承一個(gè)類
2、Python的類如果繼承了多個(gè)類,那么其尋找方法的方式有兩種,分別是:深度優(yōu)先和廣度優(yōu)先
- 當(dāng)類是經(jīng)典類時(shí),多繼承情況下,會(huì)按照深度優(yōu)先方式查找
- 當(dāng)類是新式類時(shí),多繼承情況下,會(huì)按照廣度優(yōu)先方式查找
經(jīng)典類和新式類,從字面上可以看出一個(gè)老一個(gè)新,新的必然包含了跟多的功能,也是之后推薦的寫法,從寫法上區(qū)分的話,如果 當(dāng)前類或者父類繼承了object類,那么該類便是新式類,否則便是經(jīng)典類。
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() # 執(zhí)行bar方法時(shí) # 首先去A類中查找,如果A類中沒有,則繼續(xù)去B類中找,如果B類中么有,則繼續(xù)去D類中找,如果D類中么有,則繼續(xù)去C類中找,如果還是未找到,則報(bào)錯(cuò) # 所以,查找順序:A --> B --> D --> C # 在上述查找bar方法的過程中,一旦找到,則尋找過程立即中斷,便不會(huì)再繼續(xù)找了 a.bar() 經(jīng)典類多繼承=======================================================================================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() # 執(zhí)行bar方法時(shí) # 首先去A類中查找,如果A類中沒有,則繼續(xù)去B類中找,如果B類中么有,則繼續(xù)去C類中找,如果C類中么有,則繼續(xù)去D類中找,如果還是未找到,則報(bào)錯(cuò) # 所以,查找順序:A --> B --> C --> D # 在上述查找bar方法的過程中,一旦找到,則尋找過程立即中斷,便不會(huì)再繼續(xù)找了 a.bar() 新式類多繼承經(jīng)典類:首先去A類中查找,如果A類中沒有,則繼續(xù)去B類中找,如果B類中么有,則繼續(xù)去D類中找,如果D類中么有,則繼續(xù)去C類中找,如果還是未找到,則報(bào)錯(cuò)
新式類:首先去A類中查找,如果A類中沒有,則繼續(xù)去B類中找,如果B類中么有,則繼續(xù)去C類中找,如果C類中么有,則繼續(xù)去D類中找,如果還是未找到,則報(bào)錯(cuò)
注意:在上述查找過程中,一旦找到,則尋找過程立即中斷,便不會(huì)再繼續(xù)找了。
三、多態(tài)
Pyhon不支持多態(tài)并且也用不到多態(tài),多態(tài)的概念是應(yīng)用于Java和C#這一類強(qiáng)類型語言中,而Python崇尚“鴨子類型”。
class F1: pass class S1(F1): def show(self): print 'S1.show' class S2(F1): def show(self): print 'S2.show' # 由于在Java或C#中定義函數(shù)參數(shù)時(shí),必須指定參數(shù)的類型 # 為了讓Func函數(shù)既可以執(zhí)行S1對(duì)象的show方法,又可以執(zhí)行S2對(duì)象的show方法,所以,定義了一個(gè)S1和S2類的父類 # 而實(shí)際傳入的參數(shù)是:S1對(duì)象和S2對(duì)象 def Func(F1 obj): """Func函數(shù)需要接收一個(gè)F1類型或者F1子類的類型""" print obj.show() s1_obj = S1() Func(s1_obj) # 在Func函數(shù)中傳入S1類的對(duì)象 s1_obj,執(zhí)行 S1 的show方法,結(jié)果:S1.show s2_obj = S2() Func(s2_obj) # 在Func函數(shù)中傳入Ss類的對(duì)象 ss_obj,執(zhí)行 Ss 的show方法,結(jié)果:S2.show Python偽代碼實(shí)現(xiàn)Java或C#的多態(tài)======================================================================================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 “鴨子類型”總結(jié)以上就是本節(jié)對(duì)于面向?qū)ο蟪跫?jí)知識(shí)的介紹,總結(jié)如下:
- 面向?qū)ο笫且环N編程方式,此編程方式的實(shí)現(xiàn)是基于對(duì)類和對(duì)象的使用
- 類是一個(gè)模板,模板中包裝了多個(gè)“函數(shù)”供使用
- 對(duì)象,根據(jù)模板創(chuàng)建的實(shí)例(即:對(duì)象),實(shí)例用于調(diào)用被包裝在類中的函數(shù)
- 面向?qū)ο笕筇匦?#xff1a;封裝、繼承和多態(tài)
問答專區(qū)
問題一:什么樣的代碼才是面向?qū)ο?#xff1f;
答:從簡(jiǎn)單來說,如果程序中的所有功能都是用 類 和 對(duì)象 來實(shí)現(xiàn),那么就是面向?qū)ο缶幊塘恕?/p>
問題二:函數(shù)式編程 和 面向?qū)ο?如何選擇?分別在什么情況下使用?
答:須知:對(duì)于 C# 和 Java 程序員來說不存在這個(gè)問題,因?yàn)樵搩砷T語言只支持面向?qū)ο缶幊?#xff08;不支持函數(shù)式編程)。而對(duì)于 Python 和 PHP 等語言卻同時(shí)支持兩種編程方式,且函數(shù)式編程能完成的操作,面向?qū)ο蠖伎梢詫?shí)現(xiàn);而面向?qū)ο蟮哪芡瓿傻牟僮?#xff0c;函數(shù)式編程不行(函數(shù)式編程無法實(shí)現(xiàn)面向?qū)ο蟮姆庋b功能)。
所以,一般在Python開發(fā)中,全部使用面向?qū)ο?或 面向?qū)ο蠛秃瘮?shù)式混合使用
面向?qū)ο蟮膽?yīng)用場(chǎng)景:
1.多函數(shù)需使用共同的值,如:數(shù)據(jù)庫的增、刪、改、查操作都需要連接數(shù)據(jù)庫字符串、主機(jī)名、用戶名和密碼
class SqlHelper: def __init__(self, host, user, pwd): self.host = host self.user = user self.pwd = pwd def 增(self): # 使用主機(jī)名、用戶名、密碼(self.host 、self.user 、self.pwd)打開數(shù)據(jù)庫連接 # do something # 關(guān)閉數(shù)據(jù)庫連接 def 刪(self): # 使用主機(jī)名、用戶名、密碼(self.host 、self.user 、self.pwd)打開數(shù)據(jù)庫連接 # do something # 關(guān)閉數(shù)據(jù)庫連接 def 改(self): # 使用主機(jī)名、用戶名、密碼(self.host 、self.user 、self.pwd)打開數(shù)據(jù)庫連接 # do something # 關(guān)閉數(shù)據(jù)庫連接 def 查(self): # 使用主機(jī)名、用戶名、密碼(self.host 、self.user 、self.pwd)打開數(shù)據(jù)庫連接 # do something # 關(guān)閉數(shù)據(jù)庫連接 # do something Demo2.需要?jiǎng)?chuàng)建多個(gè)事物,每個(gè)事物屬性個(gè)數(shù)相同,但是值的需求
如:張三、李四、楊五,他們都有姓名、年齡、血型,但其都是不相同。即:屬性個(gè)數(shù)相同,但值不相同
class Person: def __init__(self, name ,age ,blood_type): self.name = name self.age = age self.blood_type = blood_type def detail(self): temp = "i am %s, age %s , blood type %s " % (self.name, self.age, self.blood_type) print temp zhangsan = Person('張三', 18, 'A') lisi = Person('李四', 73, 'AB') yangwu = Person('楊五', 84, 'A') Demo問題三:類和對(duì)象在內(nèi)存中是如何保存?
答:類以及類中的方法在內(nèi)存中只有一份,而根據(jù)類創(chuàng)建的每一個(gè)對(duì)象都在內(nèi)存中需要存一份,大致如下圖:
如上圖所示,根據(jù)類創(chuàng)建對(duì)象時(shí),對(duì)象中除了封裝 name 和 age 的值之外,還會(huì)保存一個(gè)類對(duì)象指針,該值指向當(dāng)前對(duì)象的類。
當(dāng)通過 obj1 執(zhí)行 【方法一】 時(shí),過程如下:
作者:武沛齊
出處:http://www.cnblogs.com/wupeiqi/
轉(zhuǎn)載于:https://www.cnblogs.com/jaysonking/p/10521100.html
總結(jié)
以上是生活随笔為你收集整理的Python 面向对象(初级篇)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 闽政通怎么更新版本(这个字怎么读)
- 下一篇: 和平精英怎么免费获得皮肤