輕松學(xué)會(huì)python面向?qū)ο蟮?4篇---填坑,關(guān)于屬性和方法(2)

上一篇文章,我填了屬性的坑,這一篇繼續(xù)來填方法的坑。此前在介紹類時(shí),我一直強(qiáng)調(diào)類是一個(gè)約定,屬性是對數(shù)據(jù)的約定,方法是對操作數(shù)據(jù)行為的約定。python的面向?qū)ο笥?種方法,分別是實(shí)例方法,類方法,靜態(tài)方法,接下來,我將從多個(gè)角度來介紹這三種方法
1. 定義方法不同
class NovelBook():
book_type = "小說"
def __init__(self, _name, _author, _price):
self.name = _name # 書名
self.author = _author # 作者
self.price = _price # 價(jià)格
def discount(self, ratio):
"""
打折
:param ratio:
:return:
"""
self.price *= ratio
@classmethod
def out_type(cls):
"""
輸出類型
:return:
"""
print(cls.book_type)
@staticmethod
def off_the_shelf():
"""
下架
:return:
"""
print("商品下架")
從定義形式上對他們進(jìn)行區(qū)分
沒有被classmethod和staticmethod裝飾的方法就是實(shí)例方法,例如discount
被classmethod裝飾器修飾的方法就是類方法,例如out_type
被staticmethod裝飾的方法就是靜態(tài)方法,例如off_the_shelf
實(shí)例方法第一個(gè)參數(shù)通常被定義為self,類方法第一個(gè)參數(shù)通常被定義為cls,但這不是必須,僅僅是一個(gè)約定俗成的規(guī)則,因此不能作為區(qū)分它們的特征。self表示實(shí)例對象,誰調(diào)用,self就是誰;cls表示類,也遵守誰調(diào)用cls就是誰的原則,但略有不同,本文3.1小節(jié)會(huì)解釋這種不同。
__init__方法的第一個(gè)參數(shù)也是self,且也沒有被classmethod和staticmethod裝飾,但在類型劃分上,并沒有將其歸為實(shí)例方法,而是為這一類方法起了一個(gè)特定的名稱---魔法方法,關(guān)于魔法方法,后面會(huì)有專題文章進(jìn)行介紹,好吧,又給自己挖了一個(gè)坑。
2. 劃分三種方法的意義是什么?
為什么要弄出來三種不同類型的方法呢,怎樣理解他們存在的必要性呢?想要回答這個(gè)問題,先要確立一個(gè)宗旨和原則,我們所采用的種種技術(shù),一個(gè)最核心的動(dòng)機(jī)便是保護(hù)數(shù)據(jù)。

生活中,我們保護(hù)文物,保護(hù)環(huán)境,保護(hù)小動(dòng)物,在編程的世界里,我們要保護(hù)數(shù)據(jù),保護(hù)數(shù)據(jù)不被隨意修改,或者是不小心的修改。
在實(shí)例方法里,你不能修改類的屬性,在類方法里,你不能修改實(shí)例的屬性,在靜態(tài)方法里,你既不能修改實(shí)例的屬性,也不能修改類的屬性。通過這種限制,最大程度上保護(hù)了數(shù)據(jù)。
3. 如何使用他們
三種不同的方法,如何調(diào)用呢?調(diào)用的主體有兩個(gè),類,或者實(shí)例對象,下面分別介紹他們做為調(diào)用主體時(shí)的調(diào)用方法
3.1 實(shí)例作為調(diào)用主體
當(dāng)實(shí)例作為調(diào)用主體時(shí),三種方法都可以調(diào)用
book = NovelBook('西游記', '吳承恩', 99)
book.discount(0.9) # 調(diào)用實(shí)例方法
book.out_type() # 調(diào)用類方法
book.off_the_shelf() # 調(diào)用靜態(tài)方法
實(shí)例對象調(diào)用實(shí)例方法,乃是天經(jīng)地義之事,無需贅言,調(diào)用靜態(tài)方法,也是暢通無阻,唯獨(dú)調(diào)用類方法時(shí),你可能會(huì)感到一絲絲的疑惑,當(dāng)實(shí)例對象調(diào)用類方法時(shí),參數(shù)cls究竟是類還是實(shí)例對象,下面我們通過實(shí)驗(yàn)來揭開真相。
我為NovelBook類增加一個(gè)可以修改類屬性的方法
@classmethod
def set_type(cls, _type):
print(cls)
cls.book_type = _type
我在set_type方法里輸出cls的值,以此來確定當(dāng)實(shí)例對象調(diào)用它時(shí),參數(shù)cls到底是誰,執(zhí)行下面的代碼
book = NovelBook('西游記', '吳承恩', 99)
book.set_type('魔幻小說')
print(NovelBook.book_type) # 魔幻小說
輸出結(jié)果是
<class '__main__.NovelBook'>
魔幻小說
結(jié)合代碼和輸出結(jié)果進(jìn)行分析,我們可以得出一個(gè)結(jié)論:盡管是實(shí)例對象調(diào)用了類方法,但方法里的cls仍然是類,并沒有嚴(yán)格遵守誰調(diào)用cls就是誰的原則,當(dāng)實(shí)例對象調(diào)用時(shí),cls是這個(gè)實(shí)例對象的類型,誰創(chuàng)建了實(shí)例,cls就是誰。
3.2 類作為調(diào)用主體
當(dāng)類作為調(diào)用主體時(shí),三種方法都可以調(diào)用
book = NovelBook('西游記', '吳承恩', 99)
NovelBook.set_type('魔幻小說') # 調(diào)用類方法
print(NovelBook.book_type)
NovelBook.discount(book, 0.9) # 調(diào)用實(shí)例方法
NovelBook.off_the_shelf() # 調(diào)用靜態(tài)方法
類,調(diào)用類方法,乃是天經(jīng)地義之事,調(diào)用靜態(tài)方法,也無阻礙,只是在調(diào)用實(shí)例方法時(shí),要做特殊處理。當(dāng)實(shí)例對象調(diào)用實(shí)例方法時(shí),遵循誰調(diào)用,self就是誰的原則,self就是那個(gè)調(diào)用實(shí)例方法的實(shí)例對象,當(dāng)調(diào)用主體是類時(shí),由于沒有實(shí)例對象的存在,因此必須為self指定實(shí)例對象。
可不可以不指定實(shí)例對象呢?答案是可以不指定,python是解釋型語言,沒有預(yù)編譯這個(gè)過程,因此你可以將book替換成None,改成
NovelBook.discount(None, 0.9)
這樣寫,還是可以調(diào)用實(shí)例方法discount,但是,程序最終會(huì)報(bào)錯(cuò),原因在于在方法discount內(nèi),需要修改price屬性,可是self此刻并非是實(shí)例對象,而是None,可None沒有price屬性。進(jìn)一步思考,如果在實(shí)例方法里不訪問任何實(shí)例屬性,那么當(dāng)類作為調(diào)用主體時(shí),self參數(shù)就可以隨意傳參了呢?有想法,用代碼驗(yàn)證,我增加一個(gè)新的實(shí)例方法
def test_none(self, ratio):
print(ratio)
在實(shí)例方法test_none里,沒有訪問任何實(shí)例屬性,接下來,看我如何用類來調(diào)用這個(gè)實(shí)例方法
NovelBook.test_none(None, 0.9) # 0.9
NovelBook.test_none(1, 0.9) # 0.9
NovelBook.test_none('隨意傳參', 0.9) # 0.9
這樣做很有趣,但沒有人會(huì)這樣做,如果實(shí)例方法里不訪問任何實(shí)例屬性,那么就應(yīng)當(dāng)將其定義為靜態(tài)方法。靜態(tài)方法里既不訪問實(shí)例屬性,也不會(huì)訪問類屬性。你沒有必要寫一個(gè)不訪問實(shí)例屬性的實(shí)例方法,也沒有必要寫一個(gè)不訪問類屬性的類方法,當(dāng)然,如果你不怕被同行恥笑,你可以這樣做,技術(shù)上沒有任何限制。
