之前我們講了Python面向過程的一面吗蚌,現(xiàn)在我們來說說它面向?qū)ο蟮囊幻嫱纫校瑢W(xué)完這章你就會發(fā)現(xiàn),寫Python原來就是天天搞對象蚯妇,就連過程都是它的對象敷燎!那什么是面向?qū)ο蟮木幊棠兀覀兛梢院兔嫦蜻^程對比一下箩言。
舉個例子硬贯,我想吃奧利奧。從過程角度看陨收,我就要經(jīng)過扭一扭饭豹、舔一舔、泡一泡务漩、吃下去這幾個步驟才能完成吃奧利奧這個事情拄衰。從對象角度看(太詳細寫會占篇幅,這里會省略一些)饵骨,這件事參與者有手翘悉、奧利奧、舌頭居触、牛奶妖混、嘴包吝,那么首先我要得知這幾者的具體信息:手——有五個指頭,可以扭動源葫、奧利奧——一個帶餡兒的由兩個餅干構(gòu)成的夾心餅干......然后就是具體的做法了诗越,用兩只手的五指將夾心的奧利奧的兩片餅干扭動后,將兩片餅干分開息堂,餡兒會分別粘在兩個餅干上嚷狞,然后......
從這個例子我們可以發(fā)現(xiàn)幾個事情∪傺撸“扭一扭”等這些過程就好比一個個封裝好的函數(shù)床未,一個函數(shù)實現(xiàn)一個過程。但是可以看到振坚,無論哪個過程都會出現(xiàn)奧利奧這個東西薇搁,所以每個過程的關(guān)聯(lián)度很高,也即耦合性很強渡八。從對象的角度啃洋,每個角色都在做自己的動作,有自己的特點屎鳍,比如手做扭的動作宏娄,而奧利奧剛好是一個有兩個餅干的夾心,他們是完全分開的角色逮壁,所以面向?qū)ο蟮?strong>耦合性很弱孵坚。我們再看看面向?qū)ο罄锞唧w做法的描述,乍一看這不就是“扭一扭”的過程嘛窥淆。不過在看過廣告之前卖宠,一說“扭一扭”這個詞,大家肯定不知道是什么意思忧饭,因為這是對某一件事的高度抽象概括而來的幾個過程扛伍,只有仔細閱讀這個抽象的具體內(nèi)容,也就是看過廣告的眷昆,才知道“扭一扭”為何蜒秤。而這個抽象的具體內(nèi)容汁咏,是由一個個對象演繹而成的亚斋,所以說通過對象的描述,雖然復(fù)雜很多攘滩,但能夠?qū)嵲诘倪€原事物原本的過程帅刊。
所以綜上,面向?qū)ο蟮某绦蛘Z言是更加貼近人的語言漂问,而面向過程更貼近于機器赖瞒。在如今計算機性能大幅提高的今天女揭,我們越來越重視語言向人的方向發(fā)展,利用面向?qū)ο笳Z言栏饮,程序員可以更快更好的開發(fā)出新的產(chǎn)品吧兔,大大提高的應(yīng)用產(chǎn)能的發(fā)展。在對面向?qū)ο笥幸稽c了解后袍嬉,我們來學(xué)學(xué)Python面向?qū)ο蟮恼Z法境蔼。
1 給對象歸類
對象滿地球都是,什么《高等數(shù)學(xué)》伺通、貓箍土、老師......這么雜,我們首先要歸個類吧罐监,生物界還分七等呢吴藻,如果不分就沒有層次,事物看起來會很亂弓柱。比如天上這么多飛來飛去的沟堡,我們暫且歸為鳥類吧。那鳥都有什么呢矢空?比如怎么運動弦叶、吃什么、怎么吃妇多?我們來寫寫看:
class Bird():
'''
這是個鳥類
'''
move = 'fly'
food = 'meat'
def eat(self, name, thing):
print(name,' eat a ',thing,' !')
class
是“類”的關(guān)鍵字伤哺,Bird是我取的類的名字。里面就是鳥類的屬性了者祖,我們還是用三個單引號表示幫助文檔立莉,像move這類屬性稱為成員變量。利用def
關(guān)鍵字七问,我們可以定義方法蜓耻,也就是這個類的一些動作,這里指鳥吃東西的動作械巡。這里的方法其實和函數(shù)差不多刹淌,括號里面是參數(shù)件余。唯一不同就是作為類的方法舔琅,括號內(nèi)必須有self
關(guān)鍵字墙歪,是指“自己”鲫凶,也就是在方法里可以調(diào)用自己類的成員變量和方法岖免,具體調(diào)用后面講谈截。
其實Python有許多內(nèi)置方法嵌施,這些方法名字都用雙下劃線括起腿箩,我們這里講一個常用的方法挣磨,名為__init__()
雇逞。這個方法的作用是荤懂,在你利用這個類創(chuàng)建一個對象時,它會立刻執(zhí)行塘砸,比如我們現(xiàn)在改寫一下鳥類:
class Bird(object):
move = 'fly'
food = 'meat'
def __init__(self, name):
self.name = name
def eat(self, thing):
print(self.name,' eat a ',thing,' !')
這個類現(xiàn)在新增加了init方法节仿,每創(chuàng)建一次對象,這個方法便會執(zhí)行掉蔬。創(chuàng)建方法在后面講解∷诔埽現(xiàn)在我們已經(jīng)創(chuàng)建了一個比較完整的鳥類,但這個類過于寬泛眉踱,鳥類名下有很多鳥挤忙,每個鳥有自己獨特的屬性,比如羽毛顏色會不一樣谈喳,這該怎么辦册烈?Python中,類可以繼承婿禽,繼承的類包含父類中所有的屬性赏僧,也會執(zhí)行父類的內(nèi)置方法,而且我們可以重寫父類扭倾,更改父類的方法淀零。比如我們現(xiàn)在創(chuàng)建天鵝類,天鵝也是鳥的一種呀膛壹。
class Swan(Bird):
feather_color = 'white' # 新的成員變量
def eat(self): # 重寫eat方法
print('I ate a frog !')
天鵝類驾中,它繼承了鳥類的屬性外,我還添加了羽毛顏色模聋,并且重寫了吃的方法肩民。添加的屬性會在這一子類生效,而重寫的方法會代替父類的方法生效链方。我們還可以看到持痰,在定義鳥類時,括號寫的是object
祟蚀,現(xiàn)在寫的是Bird工窍,可知括號內(nèi)寫的是繼承的類,object
是對象類前酿,是所有類的總和患雏,也即是一個根類,一般第一個定義的就用object繼承薪者,而后面我們繼承鳥類纵苛,所以括號寫B(tài)ird剿涮。
2 我想要一個對象
女媧有了人的模型后捏了一堆的人從而創(chuàng)造了人類言津。我們現(xiàn)在“捏”了這么多類攻人,也該有自己的對象了。有了鳥類悬槽,現(xiàn)在就創(chuàng)建一只鳥吧怀吻,我們就叫他spring。
spring = Bird('spring')
看初婆,只需要一行代碼蓬坡,你就有一個對象了。原本只需要寫Bird()
即可磅叛,但我們類里有一個init
方法屑咳,這個方法里有一個name
參數(shù),我們需要在這里寫入弊琴,執(zhí)行完該語句后兆龙,程序會立刻執(zhí)行init
方法。在方法里敲董,我們使用了self.name
紫皇,.
是用于引用類屬性的,點前寫類的名字腋寨,后寫要調(diào)用的成員或方法即可聪铺。這里self
是自己,也就是調(diào)用成員變量name
并進行初始化萄窜。也即在執(zhí)行完這行代碼后铃剔,我們就有了name
成員變量了。現(xiàn)在我們試試調(diào)用里面的屬性和方法:
print(spring.name, spring.move, spring.food) # 調(diào)用成員變量
# 輸出: springflymeat
spring.eat('worm') # 調(diào)用成員方法
# 輸出:spring eat a worm !
我們再造一只天鵝試試:
summer = Swam('summer') # 創(chuàng)建對象
print(summer.name, summer.food, summer.feather_color) # 調(diào)用成員變量
# 輸出:summermeatwhit
summer.eat # 調(diào)用重寫方法
# 輸出:I ate a frog !
我們看到天鵝確實繼承了鳥類的屬性查刻,同時也按照重寫的eat
方法輸出番宁。所以說,我們想要一個對象赖阻,要先有它的類蝶押,再通過類創(chuàng)造對象。我們可以調(diào)用對象的方法實現(xiàn)許多事情火欧。感興趣的可以試試把吃奧利奧的事情寫寫棋电。
3 一切皆對象
雖然說Python集成了面向過程、面向?qū)ο蟮扔谝簧淼某壵Z言苇侵,但究其本身赶盔,就是面向?qū)ο螅磺械难苌际抢脤ο髮懗龅挠芘āO日f說之前學(xué)到的幾種數(shù)據(jù)結(jié)構(gòu)于未,我曾提到它有很多的函數(shù)可以用,其實這些函數(shù)恰巧是處理這些數(shù)據(jù)的方法。Python把這些數(shù)據(jù)結(jié)構(gòu)看做對象烘浦,通過對象方法進行處理抖坪。我們加下來詳細說說處理他們的方法。
3.1 列表
list.count(5) # 查看列表有幾個元素5
list.index(3) # 查詢第一個出現(xiàn)3的元素下標
list.append(6) # 在列表最后一個添加元素6
list.sort() # 排序
list.reverse() # 反轉(zhuǎn)次序
list.pop() # 刪除最后一個元素并返回該元素
list. remove(2) # 刪除第一個元素2
list. insert(0, 9) # 在第0位置插入元素2
list. clear() # 清空列表
3.2 元組和字符串
tuple.count(5) # 查看元組有幾個元素5
tuple.index(3) # 查詢元組第一個出現(xiàn)3的元素下標
str = 'hello world!'
sub = 'world'
str.count(sub) # 返回sub在str出現(xiàn)的次數(shù)
str. find(sub) # 從左開始闷叉,返回找到第一個sub的第一個字符的位置擦俐,沒有返回-1
str. index(sub) # 從左開始,返回找到的第一個sub的索引握侧,沒有則報錯
str. rfind(sub) # 從右開始蚯瞧,返回找到第一個sub的第一個字符的位置,沒有返回-1
str. rindex(sub) # 從右開始品擎,返回找到的第一個sub的索引埋合,沒有則報錯
str. isalnum() # 查看是否所有字符都是字母或數(shù)字,是則返回True萄传,否則False
str. isalpha() # 查看是否所有字符都是字母饥悴,是則返回True,否則False
str. isdigit() # 查看是否所有字符都是數(shù)字盲再,是則返回True西设,否則False
str. istitle() # 查看是否所有字符都是首字母大寫,是則返回True答朋,否則False
str. isspace() # 查看是否所有字符都是空格贷揽,是則返回True,否則False
str. islower() # 查看是否所有字符都是梦碗,都是小寫禽绪,是則返回True,否則False
str. isupper() # 查看是否所有字符都是大寫洪规,是則返回True印屁,否則False
str. splite([sep, [max]]) # 以sep為分隔符分隔字符串max次,默認用空格斩例,都為選填
str. rsplite([sep, [max]]) # 從右開始分隔
str. join(s) # 返回以str分隔的s
str. strip([cub]) # 去掉字符串去掉開頭結(jié)尾的sub雄人,默認為空格
str. replace(sub, new_sub) # 用new_sub代替字符串中的sub
str. capitalize() # 返回開頭字母大寫的字符串
str. lower() # 返回所有字母小寫的字符串
str. upper() # 返回所有字母大寫的字符串
str. swapcase() # 返回大小寫全部反轉(zhuǎn)的字符串
str. title() # 將開頭字母全部大寫(以空格分開)
str. center(width) # 在width長度內(nèi)將字符串居中
str. ljust(width) # 在width長度內(nèi)將字符串居右
str. rjust(width) # 在width長度內(nèi)將字符串居左
3.3 詞典
dict.values() # 返回詞典所有值(可用于循環(huán)遍歷)
dict.keys() # 返回詞典所有鍵(可用于循環(huán)遍歷)
dict.clear() # 清空詞典
不光是這些數(shù)據(jù)結(jié)構(gòu),就連面向過程的一些方法念赶,都是用面向?qū)ο髮崿F(xiàn)的础钠。
3.4 循環(huán)
每一個for
循環(huán)都會自動尋找下一個變量的元素,事實上叉谜,它是通過迭代器和迭代器中的__next__()
方法實現(xiàn)循環(huán)的旗吁,只是for循環(huán)就是運用這個原理,可以省略這樣的寫法停局。當next方法后已經(jīng)是末尾元素很钓,系統(tǒng)會返回StopIteration異常告知循環(huán)結(jié)束香府。我們可以嘗試利用面向?qū)ο蟮姆椒ㄖ苯訉崿F(xiàn)循環(huán):
list = [1, 2]
iterator = iter(list) # iter是迭代器轉(zhuǎn)換函數(shù),可以將變量轉(zhuǎn)換成迭代器
iterator.next()
iterator.__next__()
iterator.__next__() # 報錯
如果我們想從頭再次開始码倦,則需要重新定義一個迭代器企孩。上面的寫法其實可直接用for循環(huán)實現(xiàn):
for i in [1, 2]:
循環(huán)對象對于處理龐大數(shù)據(jù)的循環(huán)是有好處的,它可以邊遍歷叹洲,邊導(dǎo)入變量的元素柠硕,這樣可以讓計算機少占用內(nèi)存工禾。其實我們可以借助生成器實現(xiàn)自定義循環(huán)對象运提,可以一次性循環(huán)多種不同的元素。生成器是一個函數(shù)闻葵,用def
關(guān)鍵字定義民泵,內(nèi)部每完成一種對象的定義就可以用yield
關(guān)鍵字截斷一次,程序會對這個元素先做一次循環(huán)遍歷槽畔,再找下一個栈妆。舉個例子:
def gen():
a = 100
yield a
a = a*8
yield a
yield 1000
for i in gen():
print(i)
可以看到,for
循環(huán)首先截斷在第一個a的元素并做一次循環(huán)厢钧,并用相同的方法進行后兩次循環(huán)相當于將數(shù)據(jù)分成3組循環(huán)鳞尔,在數(shù)據(jù)量龐大的情況下,可以節(jié)省內(nèi)存早直。
3.5 函數(shù)
函數(shù)也是一個對象寥假,事實上它是利用創(chuàng)建類里的__call__()
方法實現(xiàn)的,我們可以看看:
class function(object):
def __call__(self, a)
return a+5
其實這種寫法完全等價于:
def function(a):
return a+5
只是后者本質(zhì)上還是調(diào)用前者的方法實現(xiàn)的霞扬,之后我們只需用正常的方法調(diào)用:
print(function(3)) # 輸出8
所以說Python的函數(shù)其實是使用面向?qū)ο蟮姆椒▽崿F(xiàn)的糕韧。
3.6 模塊和異常
我們之前講到許多調(diào)用函數(shù)的方法,包括自己定義的喻圃,以及Python庫萤彩。事實上他們也可以看做對象,所以這些庫又可以稱為模塊斧拍,我們是利用import
和from
這兩個方法調(diào)用這些模塊雀扶。那么同理,異常也是對象肆汹,所以異常對象其實也有一些方法怕吴,下面舉例:
try:
a = 1/0
except :
ZeroDivisionError as e:
print('catch ZeroDivisionError')
print(e.message)
這里首先我們利用as
關(guān)鍵字,將ZeroDivisionError
這個錯誤名稱用e代替县踢,事實上這個在其他地方也可以用转绷。我們調(diào)用e
對象的message
方法,查看異常信息硼啤。
具體實例代碼可以看我的碼云:第四章樣例代碼