Python 面向?qū)ο缶幊谈攀?/h2>

類與實(shí)例

類與實(shí)例相互關(guān)聯(lián)著:類是對(duì)象的定義,而實(shí)例是“真正的實(shí)物”,它存放了類中所定義的對(duì)象的具體信息。

下面的示例展示了如何創(chuàng)建一個(gè)類:

class MyNewObjectType(bases):
      ''' 創(chuàng)建 MyNewObjectType 類'''
      class_suite

關(guān)鍵字是 class,緊接著一個(gè)類名。隨后是定義類的類代碼。這里通常由各種各樣的定義和聲明組成。新式類和經(jīng)典類聲明的最大不同在于,所有新式類必須繼承至少一個(gè)父類,參數(shù) bases 可以是一個(gè)(單繼承)或多個(gè)(多重繼承)用于繼承的父類。

創(chuàng)建一個(gè)實(shí)例的過程稱作實(shí)例化,過程如下(注意:沒有使用 new 關(guān)鍵字):

myFirstObject = MyNewObjectType()

類名使用我們所熟悉的函數(shù)操作符(()),以“函數(shù)調(diào)用”的形式出現(xiàn)。然后你通常會(huì)把這個(gè)新建的實(shí)例賦給一個(gè)變量。賦值在語法上不是必須的,但如果你沒有把這個(gè)實(shí)例保存到一個(gè)變量中,它就沒用了,會(huì)被自動(dòng)垃圾收集器回收,因?yàn)槿魏我弥赶蜻@個(gè)實(shí)例。這樣,你剛剛所做的一切,就是為那個(gè)實(shí)例分配了一塊內(nèi)存,隨即又釋放了它。

類既可以很簡單,也可以很復(fù)雜,這全憑你的需要。最簡單的情況,類僅用作名稱空間(namespace)。這意味著你把數(shù)據(jù)保存在變量中,對(duì)他們按名稱空間進(jìn)行分組,使得他們處于同樣的關(guān)系空間中——所謂的關(guān)系是使用標(biāo)準(zhǔn) Python 句點(diǎn)屬性標(biāo)識(shí)。例如,你有一個(gè)本身沒有任何屬性的類,使用它僅對(duì)數(shù)據(jù)提供一個(gè)名字空間,讓你的類擁有像 C 語言中的結(jié)構(gòu)體(structure)一樣的特性,或者換句話說,這樣的類僅作為容器對(duì)象來共享名字空間。

示例如下:

class MyData(object):
    pass

mathObj = MyData()
mathObj.x = 4
mathObj.y = 5
mathObj.x + mathObj.y
9
mathObj.x \\* mathObj.y
20

方法

在 Python 中,方法定義在類定義中,但只能被實(shí)例所調(diào)用。也就是說,調(diào)用一個(gè)方法的最終途徑必須是這樣的:(1)定義類(和方法);(2)創(chuàng)建一個(gè)實(shí)例;(3)最后一步,用這個(gè)實(shí)例調(diào)用方法。例如:

class MyDataWithMethod(object): # 定義類
    def printFoo(self): # 定義方法
        print 'You invoked printFoo()!'

這里的 self 參數(shù),它在所有的方法聲明中都存在。這個(gè)參數(shù)代表實(shí)例對(duì)象本身,當(dāng)你用實(shí)例調(diào)用方法時(shí),由解釋器傳遞給方法的,所以,你不需要自己傳遞 self 進(jìn)來,因?yàn)樗亲詣?dòng)傳入的。

舉例說明一下,假如你有一個(gè)帶兩參數(shù)的方法,所有你的調(diào)用只需要傳遞第二個(gè)參數(shù)。

下面是實(shí)例化這個(gè)類,并調(diào)用那個(gè)方法:

> > > myObj = MyDataWithMethod()
> > > myObj.printFoo()
You invoked printFoo()!

\\init\\(),是一個(gè)特殊的方法。在 Python 中, \\init\\() 實(shí)際上不是一個(gè)構(gòu)造器。你沒有調(diào)用“new”來創(chuàng)建一個(gè)新對(duì)象。(Python 根本就沒有“new”這個(gè)關(guān)鍵字)。取而代之, Python 創(chuàng)建實(shí)例后,在實(shí)例化過程中,調(diào)用 \\init\\()方法,當(dāng)一個(gè)類被實(shí)例化時(shí),就可以定義額外的行為,比如,設(shè)定初始值或者運(yùn)行一些初步診斷代碼——主要是在實(shí)例被創(chuàng)建后,實(shí)例化調(diào)用返回這個(gè)實(shí)例之前,去執(zhí)行某些特定的任務(wù)或設(shè)置。

創(chuàng)建一個(gè)類(類定義)

class AddrBookEntry(object):
    '''address book entry class'''
    def __init__(self, nm, ph): # 定義構(gòu)造器
        self.name = nm              # 設(shè)置 name
        self.phone = ph             # 設(shè)置 phone
        print 'Created instance for:', self.name

    def updatePhone(self, newph):   # 定義方法
        self.phone = newph
        print 'Updated phone# for: ', self.name

在 AddrBookEntry 類的定義中,定義了兩個(gè)方法: \\init\\()和updatePhone()。\\init\\()在實(shí)例化時(shí)被調(diào)用,即,在AddrBookEntry()被調(diào)用時(shí)。你可以認(rèn)為實(shí)例化是對(duì) \\init\\()的一種隱式的調(diào)用,因?yàn)閭鹘oAddrBookEntry()的參數(shù)完全與\\init\\()接收到的參數(shù)是一樣的(除了self,它是自動(dòng)傳遞的)。

創(chuàng)建實(shí)例(實(shí)例化)

> > > john = AddrBookEntry('John Doe', '408-555-1212') # 為 John Doe 創(chuàng)建實(shí)例
> > > jane = AddrBookEntry('Jane Doe', '650-555-1212') # 為 Jane Doe 創(chuàng)建實(shí)例

這就是實(shí)例化調(diào)用,它會(huì)自動(dòng)調(diào)用 \\init\\()。 self 把實(shí)例對(duì)象自動(dòng)傳入\\init\\()。

另外,如果不存在默認(rèn)的參數(shù),那么傳給 \\init\\() 的兩個(gè)參數(shù)在實(shí)例化時(shí)是必須的。

訪問實(shí)例屬性

> > > john
> > > john.name
> > > jane.name
> > > jane.phone

一旦實(shí)例被創(chuàng)建后,就可以證實(shí)一下,在實(shí)例化過程中,我們的實(shí)例屬性是否確實(shí)被 \\init\\() 設(shè)置了。我們可以通過解釋器“轉(zhuǎn)儲(chǔ)”實(shí)例來查看它是什么類型的對(duì)象。

方法調(diào)用(通過實(shí)例)

> > > john.updatePhone('415-555-1212')    # 更新 John Doe 的電話
> > > john.phone

updatePhone()方法需要一個(gè)參數(shù)(不計(jì) self 在內(nèi)):新的電話號(hào)碼。在 updatePhone()之后,立即檢查實(shí)例屬性,可以證實(shí)已生效。

創(chuàng)建子類

靠繼承來進(jìn)行子類化是創(chuàng)建和定制新類型的一種方式,新的類將保持已存在類所有的特性,而不會(huì)改動(dòng)原來類的定義。對(duì)于新類類型而言,這個(gè)新的子類可以定制只屬于它的特定功能。除了與父類或基類的關(guān)系外,子類與通常的類沒有什么區(qū)別,也像一般類一樣進(jìn)行實(shí)例化。注意下面,子類聲明中提到了父類:

class EmplAddrBookEntry(AddrBookEntry):
    '''Employee Address Book Entry class''' # 員工地址簿類
    def __init__(self, nm, ph, id, em):
        AddrBookEntry.__init__(self, nm, ph)
        self.empid = id
        self.email = em
    
    def updateEmail(self, newem):
        self.email = newem
        print 'Updated e-mail address for:', self.name

現(xiàn)在我們創(chuàng)建了第一個(gè)子類, EmplAddrBookEntry。 Python 中,當(dāng)一個(gè)類被派生出來,子類就繼承了基類的屬性,所以,在上面的類中,我們不僅定義了 \\init\\(),UpdateEmail()方法,而且 EmplAddrBookEntry 還從 AddrBookEntry 中繼承了 updatePhone()方法。

如果需要,每個(gè)子類最好定義它自己的構(gòu)造器,不然,基類的構(gòu)造器會(huì)被調(diào)用。然而,如果子類重寫基類的構(gòu)造器,基類的構(gòu)造器就不會(huì)被自動(dòng)調(diào)用了——這樣,基類的構(gòu)造器就必須顯式寫出才會(huì)被執(zhí)行,就像我們上面那樣,用AddrBookEntry.\\init\\()設(shè)置名字和電話號(hào)碼。我們的子類在構(gòu)造器后面幾行還設(shè)置了另外兩個(gè)實(shí)例屬性:員工ID和電子郵件地址。

注意,這里我們要顯式傳遞 self 實(shí)例對(duì)象給基類構(gòu)造器,因?yàn)槲覀儾皇窃谠搶?shí)例中而是在一個(gè)子類實(shí)例中調(diào)用那個(gè)方法。因?yàn)槲覀儾皇峭ㄟ^實(shí)例來調(diào)用它,這種未綁定的方法調(diào)用需要傳遞一個(gè)適當(dāng)?shù)膶?shí)例(self)給方法。

使用子類

> > > john = EmplAddrBookEntry('John Doe', '408-555-1212', 42, 'john@spam.doe')
> > > john
> > > john.name
> > > john.phone
> > > john.email
> > > john.updatePhone('415-555-1212')
> > > john.phone
> > > john.updateEmail('john@doe.spam')
> > > john.email

一點(diǎn)筆記:

命名類、屬性和方法
類名通常由大寫字母打頭。這是標(biāo)準(zhǔn)慣例,可以幫助你識(shí)別類,特別是在實(shí)例化過程中(有時(shí)看起來像函數(shù)調(diào)用)。還有,數(shù)據(jù)屬性聽起來應(yīng)當(dāng)是數(shù)據(jù)值的名字,方法名應(yīng)當(dāng)指出對(duì)應(yīng)對(duì)象或值的行為。另一種表達(dá)方式是:數(shù)據(jù)值應(yīng)該使用名詞作為名字,方法使用謂詞(動(dòng)詞加對(duì)象)。數(shù)據(jù)項(xiàng)是操作的對(duì)象、方法應(yīng)當(dāng)表明程序員想要在對(duì)象進(jìn)行什么操作。在上面我們定義的類中,遵循了這樣的方針,數(shù)據(jù)值像“name”, “phone”和“email”,行為如“updatePhone”, “updateEmail”。這就是常說的“混合記法(mixedCase)”或者“駱駝?dòng)浄ǎ╟amelCase)”?!癙ython Style Guide” 推薦使用駱駝?dòng)浄ǖ南聞澗€方式,比如,“update\_phone”, “update\_email”。類也要細(xì)致命名,像“AddrBookEntry”、“RepairShop”等就是很好的名字。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容