国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

SICP Python 描述 2.5 面向?qū)ο缶幊?

starsfun / 1956人閱讀

摘要:類似消息傳遞中的分發(fā)字典,對(duì)象響應(yīng)行為請(qǐng)求。消息傳遞和點(diǎn)表達(dá)式方法定義在類中,而實(shí)例屬性通常在構(gòu)造器中賦值,二者都是面向?qū)ο缶幊痰幕驹亍J褂脦в袃?nèi)建對(duì)象系統(tǒng)語(yǔ)言的優(yōu)點(diǎn)是,消息傳遞能夠和其它語(yǔ)言特性,例如賦值語(yǔ)句無(wú)縫對(duì)接。

2.5 面向?qū)ο缶幊?/b>

來(lái)源:2.5 Object-Oriented Programming

譯者:飛龍

協(xié)議:CC BY-NC-SA 4.0

面向?qū)ο缶幊蹋∣OP)是一種用于組織程序的方法,它組合了這一章引入的許多概念。就像抽象數(shù)據(jù)類型那樣,對(duì)象創(chuàng)建了數(shù)據(jù)使用和實(shí)現(xiàn)之間的抽象界限。類似消息傳遞中的分發(fā)字典,對(duì)象響應(yīng)行為請(qǐng)求。就像可變的數(shù)據(jù)結(jié)構(gòu),對(duì)象擁有局部狀態(tài),并且不能直接從全局環(huán)境訪問。Python 對(duì)象系統(tǒng)提供了新的語(yǔ)法,更易于為組織程序?qū)崿F(xiàn)所有這些實(shí)用的技巧。

但是對(duì)象系統(tǒng)不僅僅提供了便利;它也為程序設(shè)計(jì)添加了新的隱喻,其中程序中的幾個(gè)部分彼此交互。每個(gè)對(duì)象將局部狀態(tài)和行為綁定,以一種方式在數(shù)據(jù)抽象背后隱藏二者的復(fù)雜性。我們的約束程序的例子通過在約束和連接器之前傳遞消息,產(chǎn)生了這種隱喻。Python 對(duì)象系統(tǒng)使用新的途徑擴(kuò)展了這種隱喻,來(lái)表達(dá)程序的不同部分如何互相關(guān)聯(lián),以及互相通信。對(duì)象不僅僅會(huì)傳遞消息,還會(huì)和其它相同類型的對(duì)象共享行為,以及從相關(guān)的類型那里繼承特性。

面向?qū)ο缶幊痰姆妒绞褂米约旱脑~匯來(lái)強(qiáng)化對(duì)象隱喻。我們已經(jīng)看到了,對(duì)象是擁有方法和屬性的數(shù)據(jù)值,可以通過點(diǎn)運(yùn)算符來(lái)訪問。每個(gè)對(duì)象都擁有一個(gè)類型,叫做類。Python 中可以定義新的類,就像定義函數(shù)那樣。

2.5.1 對(duì)象和類

類可以用作所有類型為該類的對(duì)象的模板。每個(gè)對(duì)象都是某個(gè)特定類的實(shí)例。我們目前使用的對(duì)象都擁有內(nèi)建類型,但是我們可以定義新的類,就像定義函數(shù)那樣。類的定義規(guī)定了在該類的對(duì)象之間共享的屬性和方法。我們會(huì)通過重新觀察銀行賬戶的例子,來(lái)介紹類的語(yǔ)句。

在介紹局部狀態(tài)時(shí),我們看到,銀行賬戶可以自然地建模為擁有balance的可變值。銀行賬戶對(duì)象應(yīng)該擁有withdraw方法,在可用的情況下,它會(huì)更新賬戶余額,并返回所請(qǐng)求的金額。我們希望添加一些額外的行為來(lái)完善賬戶抽象:銀行賬戶應(yīng)該能夠返回它的當(dāng)前余額,返回賬戶持有者的名稱,以及接受存款。

Account類允許我們創(chuàng)建銀行賬戶的多個(gè)實(shí)例。創(chuàng)建新對(duì)象實(shí)例的動(dòng)作被稱為實(shí)例化該類。Python 中實(shí)例化某個(gè)類的語(yǔ)法類似于函數(shù)的調(diào)用語(yǔ)句。這里,我們使用參數(shù)"Jim"(賬戶持有者的名稱)來(lái)調(diào)用Account

>>> a = Account("Jim")

對(duì)象的屬性是和對(duì)象關(guān)聯(lián)的名值對(duì),它可以通過點(diǎn)運(yùn)算符來(lái)訪問。屬性特定于具體的對(duì)象,而不是類的所有對(duì)象,也叫做實(shí)例屬性。每個(gè)Account對(duì)象都擁有自己的余額和賬戶持有者名稱,它們是實(shí)例屬性的一個(gè)例子。在更寬泛的編程社群中,實(shí)例屬性可能也叫做字段、屬性或者實(shí)例變量。

>>> a.holder
"Jim"
>>> a.balance
0

操作對(duì)象或執(zhí)行對(duì)象特定計(jì)算的函數(shù)叫做方法。方法的副作用和返回值可能依賴或改變對(duì)象的其它屬性。例如,depositAccount對(duì)象a上的方法。它接受一個(gè)參數(shù),即需要存入的金額,修改對(duì)象的balance屬性,并返回產(chǎn)生的余額。

>>> a.deposit(15)
15

在 OOP 中,我們說(shuō)方法可以在特定對(duì)象上調(diào)用。作為調(diào)用withdraw方法的結(jié)果,要么取錢成功,余額減少并返回,要么請(qǐng)求被拒絕,賬戶打印出錯(cuò)誤信息。

>>> a.withdraw(10)  # The withdraw method returns the balance after withdrawal
5
>>> a.balance       # The balance attribute has changed
5
>>> a.withdraw(10)
"Insufficient funds"

像上面展示的那樣,方法的行為取決于對(duì)象屬性的改變。兩次以相同參數(shù)對(duì)withdraw的調(diào)用返回了不同的結(jié)果。

2.5.2 類的定義

用戶定義的類由class語(yǔ)句創(chuàng)建,它只包含單個(gè)子句。類的語(yǔ)句定義了類的名稱和基類(會(huì)在繼承那一節(jié)討論),之后包含了定義類屬性的語(yǔ)句組:

class ():
    

當(dāng)類的語(yǔ)句被執(zhí)行時(shí),新的類會(huì)被創(chuàng)建,并且在當(dāng)前環(huán)境第一幀綁定到上。之后會(huì)執(zhí)行語(yǔ)句組。任何名稱都會(huì)在class語(yǔ)句的中綁定,通過def或賦值語(yǔ)句,創(chuàng)建或修改類的屬性。

類通常圍繞實(shí)例屬性來(lái)組織,實(shí)例屬性是名值對(duì),不和類本身關(guān)聯(lián)但和類的每個(gè)對(duì)象關(guān)聯(lián)。通過為實(shí)例化新對(duì)象定義方法,類規(guī)定了它的對(duì)象的實(shí)例屬性。

class語(yǔ)句的部分包含def語(yǔ)句,它們?yōu)樵擃惖膶?duì)象定義了新的方法。用于實(shí)例化對(duì)象的方法在 Python 中擁有特殊的名稱,__init__init兩邊分別有兩個(gè)下劃線),它叫做類的構(gòu)造器。

>>> class Account(object):
        def __init__(self, account_holder):
            self.balance = 0
            self.holder = account_holder

Account__init__方法有兩個(gè)形參。第一個(gè)是self,綁定到新創(chuàng)建的Account對(duì)象上。第二個(gè)參數(shù),account_holder,在被調(diào)用來(lái)實(shí)例化的時(shí)候,綁定到傳給該類的參數(shù)上。

構(gòu)造器將實(shí)例屬性名稱balance0綁定。它也將屬性名稱holder綁定到account_holder上。形參account_holder__init__方法的局部名稱。另一方面,通過最后一個(gè)賦值語(yǔ)句綁定的名稱holder是一直存在的,因?yàn)樗褂命c(diǎn)運(yùn)算符被存儲(chǔ)為self的屬性。

定義了Account類之后,我們就可以實(shí)例化它:

>>> a = Account("Jim")

這個(gè)對(duì)Account類的“調(diào)用”創(chuàng)建了新的對(duì)象,它是Account的實(shí)例,之后以兩個(gè)參數(shù)調(diào)用了構(gòu)造函數(shù)__init__:新創(chuàng)建的對(duì)象和字符串"Jim"。按照慣例,我們使用名稱self來(lái)命名構(gòu)造器的第一個(gè)參數(shù),因?yàn)樗壎ǖ搅吮粚?shí)例化的對(duì)象上。這個(gè)慣例在幾乎所有 Python 代碼中都適用。

現(xiàn)在,我們可以使用點(diǎn)運(yùn)算符來(lái)訪問對(duì)象的balanceholder

>>> a.balance
0
>>> a.holder
"Jim"

身份。每個(gè)新的賬戶實(shí)例都有自己的余額屬性,它的值獨(dú)立于相同類的其它對(duì)象。

>>> b = Account("Jack")
>>> b.balance = 200
>>> [acc.balance for acc in (a, b)]
[0, 200]

為了強(qiáng)化這種隔離,每個(gè)用戶定義類的實(shí)例對(duì)象都有個(gè)獨(dú)特的身份。對(duì)象身份使用isis not運(yùn)算符來(lái)比較。

>>> a is a
True
>>> a is not b
True

雖然由同一個(gè)調(diào)用來(lái)構(gòu)造,綁定到ab的對(duì)象并不相同。通常,使用賦值將對(duì)象綁定到新名稱并不會(huì)創(chuàng)建新的對(duì)象。

>>> c = a
>>> c is a
True

用戶定義類的新對(duì)象只在類(比如Account)使用調(diào)用表達(dá)式被實(shí)例化的時(shí)候創(chuàng)建。

方法。對(duì)象方法也由class語(yǔ)句組中的def語(yǔ)句定義。下面,depositwithdraw都被定義為Account類的對(duì)象上的方法:

>>> class Account(object):
        def __init__(self, account_holder):
            self.balance = 0
            self.holder = account_holder
        def deposit(self, amount):
            self.balance = self.balance + amount
            return self.balance
        def withdraw(self, amount):
            if amount > self.balance:
                return "Insufficient funds"
            self.balance = self.balance - amount
            return self.balance

雖然方法定義和函數(shù)定義在聲明方式上并沒有區(qū)別,方法定義有不同的效果。由class語(yǔ)句中的def語(yǔ)句創(chuàng)建的函數(shù)值綁定到了聲明的名稱上,但是只在類的局部綁定為一個(gè)屬性。這個(gè)值可以使用點(diǎn)運(yùn)算符在類的實(shí)例上作為方法來(lái)調(diào)用。

每個(gè)方法定義同樣包含特殊的首個(gè)參數(shù)self,它綁定到方法所調(diào)用的對(duì)象上。例如,讓我們假設(shè)deposit在特定的Account對(duì)象上調(diào)用,并且傳遞了一個(gè)對(duì)象值:要存入的金額。對(duì)象本身綁定到了self上,而參數(shù)綁定到了amount上。所有被調(diào)用的方法能夠通過self參數(shù)來(lái)訪問對(duì)象,所以它們可以訪問并操作對(duì)象的狀態(tài)。

為了調(diào)用這些方法,我們?cè)俅问褂命c(diǎn)運(yùn)算符,就像下面這樣:

>>> tom_account = Account("Tom")
>>> tom_account.deposit(100)
100
>>> tom_account.withdraw(90)
10
>>> tom_account.withdraw(90)
"Insufficient funds"
>>> tom_account.holder
"Tom"

當(dāng)一個(gè)方法通過點(diǎn)運(yùn)算符調(diào)用時(shí),對(duì)象本身(這個(gè)例子中綁定到了tom_account)起到了雙重作用。首先,它決定了withdraw意味著哪個(gè)名稱;withdraw并不是環(huán)境中的名稱,而是Account類局部的名稱。其次,當(dāng)withdraw方法調(diào)用時(shí),它綁定到了第一個(gè)參數(shù)self上。求解點(diǎn)運(yùn)算符的詳細(xì)過程會(huì)在下一節(jié)中展示。

2.5.3 消息傳遞和點(diǎn)表達(dá)式

方法定義在類中,而實(shí)例屬性通常在構(gòu)造器中賦值,二者都是面向?qū)ο缶幊痰幕驹亍_@兩個(gè)概念很大程度上類似于數(shù)據(jù)值的消息傳遞實(shí)現(xiàn)中的分發(fā)字典。對(duì)象使用點(diǎn)運(yùn)算符接受消息,但是消息并不是任意的、值為字符串的鍵,而是類的局部名稱。對(duì)象也擁有具名的局部狀態(tài)值(實(shí)例屬性),但是這個(gè)狀態(tài)可以使用點(diǎn)運(yùn)算符訪問和操作,并不需要在實(shí)現(xiàn)中使用nonlocal語(yǔ)句。

消息傳遞的核心概念,就是數(shù)據(jù)值應(yīng)該通過響應(yīng)消息而擁有行為,這些消息和它們所表示的抽象類型相關(guān)。點(diǎn)運(yùn)算符是 Python 的語(yǔ)法特征,它形成了消息傳遞的隱喻。使用帶有內(nèi)建對(duì)象系統(tǒng)語(yǔ)言的優(yōu)點(diǎn)是,消息傳遞能夠和其它語(yǔ)言特性,例如賦值語(yǔ)句無(wú)縫對(duì)接。我們并不需要不同的消息來(lái)“獲取”和“設(shè)置”關(guān)聯(lián)到局部屬性名稱的值;語(yǔ)言的語(yǔ)法允許我們直接使用消息名稱。

點(diǎn)表達(dá)式。類似tom_account.deposit的代碼片段叫做點(diǎn)表達(dá)式。點(diǎn)表達(dá)式包含一個(gè)表達(dá)式,一個(gè)點(diǎn)和一個(gè)名稱:

 . 

可為任意的 Python 有效表達(dá)式,但是必須是個(gè)簡(jiǎn)單的名稱(而不是求值為name的表達(dá)式)。點(diǎn)表達(dá)式會(huì)使用提供的,對(duì)值為的對(duì)象求出屬性的值。

內(nèi)建的函數(shù)getattr也會(huì)按名稱返回對(duì)象的屬性。它是等價(jià)于點(diǎn)運(yùn)算符的函數(shù)。使用getattr,我們就能使用字符串來(lái)查找某個(gè)屬性,就像分發(fā)字典那樣:

>>> getattr(tom_account, "balance")
10

我們也可以使用hasattr測(cè)試對(duì)象是否擁有某個(gè)具名屬性:

>>> hasattr(tom_account, "deposit")
True

對(duì)象的屬性包含所有實(shí)例屬性,以及所有定義在類中的屬性(包括方法)。方法是需要特別處理的類的屬性。

方法和函數(shù)。當(dāng)一個(gè)方法在對(duì)象上調(diào)用時(shí),對(duì)象隱式地作為第一個(gè)參數(shù)傳遞給方法。也就是說(shuō),點(diǎn)運(yùn)算符左邊值為的對(duì)象,會(huì)自動(dòng)傳給點(diǎn)運(yùn)算符右邊的方法,作為第一個(gè)參數(shù)。所以,對(duì)象綁定到了參數(shù)self上。

為了自動(dòng)實(shí)現(xiàn)self的綁定,Python 區(qū)分函數(shù)和綁定方法。我們已經(jīng)在這門課的開始創(chuàng)建了前者,而后者在方法調(diào)用時(shí)將對(duì)象和函數(shù)組合到一起。綁定方法的值已經(jīng)將第一個(gè)函數(shù)關(guān)聯(lián)到所調(diào)用的實(shí)例,當(dāng)方法調(diào)用時(shí)實(shí)例會(huì)被命名為self

通過在點(diǎn)運(yùn)算符的返回值上調(diào)用type,我們可以在交互式解釋器中看到它們的差異。作為類的屬性,方法只是個(gè)函數(shù),但是作為實(shí)例屬性,它是綁定方法:

>>> type(Account.deposit)

>>> type(tom_account.deposit)

這兩個(gè)結(jié)果的唯一不同點(diǎn)是,前者是個(gè)標(biāo)準(zhǔn)的二元函數(shù),帶有參數(shù)selfamount。后者是一元方法,當(dāng)方法被調(diào)用時(shí),名稱self自動(dòng)綁定到了名為tom_account的對(duì)象上,而名稱amount會(huì)被綁定到傳遞給方法的參數(shù)上。這兩個(gè)值,無(wú)論函數(shù)值或綁定方法的值,都和相同的deposit函數(shù)體所關(guān)聯(lián)。

我們可以以兩種方式調(diào)用deposit:作為函數(shù)或作為綁定方法。在前者的例子中,我們必須為self參數(shù)顯式提供實(shí)參。而對(duì)于后者,self參數(shù)已經(jīng)自動(dòng)綁定了。

>>> Account.deposit(tom_account, 1001)  # The deposit function requires 2 arguments
1011
>>> tom_account.deposit(1000)           # The deposit method takes 1 argument
2011

函數(shù)getattr的表現(xiàn)就像運(yùn)算符那樣:它的第一個(gè)參數(shù)是對(duì)象,而第二個(gè)參數(shù)(名稱)是定義在類中的方法。之后,getattr返回綁定方法的值。另一方面,如果第一個(gè)參數(shù)是個(gè)類,getattr會(huì)直接返回屬性值,它僅僅是個(gè)函數(shù)。

實(shí)踐指南:命名慣例。類名稱通常以首字母大寫來(lái)編寫(也叫作駝峰拼寫法,因?yàn)槊Q中間的大寫字母像駝峰)。方法名稱遵循函數(shù)命名的慣例,使用以下劃線分隔的小寫字母。

有的時(shí)候,有些實(shí)例變量和方法的維護(hù)和對(duì)象的一致性相關(guān),我們不想讓用戶看到或使用它們。它們并不是由類定義的一部分抽象,而是一部分實(shí)現(xiàn)。Python 的慣例規(guī)定,如果屬性名稱以下劃線開始,它只能在方法或類中訪問,而不能被類的用戶訪問。

2.5.4 類屬性

有些屬性值在特定類的所有對(duì)象之間共享。這樣的屬性關(guān)聯(lián)到類本身,而不是類的任何獨(dú)立實(shí)例。例如,讓我們假設(shè)銀行以固定的利率對(duì)余額支付利息。這個(gè)利率可能會(huì)改變,但是它是在所有賬戶中共享的單一值。

類屬性由class語(yǔ)句組中的賦值語(yǔ)句創(chuàng)建,位于任何方法定義之外。在更寬泛的開發(fā)者社群中,類屬性也被叫做類變量或靜態(tài)變量。下面的類語(yǔ)句以名稱interestAccount創(chuàng)建了類屬性。

>>> class Account(object):
        interest = 0.02            # A class attribute
        def __init__(self, account_holder):
            self.balance = 0
            self.holder = account_holder
        # Additional methods would be defined here

這個(gè)屬性仍舊可以通過類的任何實(shí)例來(lái)訪問。

>>> tom_account = Account("Tom")
>>> jim_account = Account("Jim")
>>> tom_account.interest
0.02
>>> jim_account.interest
0.02

但是,對(duì)類屬性的單一賦值語(yǔ)句會(huì)改變所有該類實(shí)例上的屬性值。

>>> Account.interest = 0.04
>>> tom_account.interest
0.04
>>> jim_account.interest
0.04

屬性名稱。我們已經(jīng)在我們的對(duì)象系統(tǒng)中引入了足夠的復(fù)雜性,我們需要規(guī)定名稱如何解析為特定的屬性。畢竟,我們可以輕易擁有同名的類屬性和實(shí)例屬性。

像我們看到的那樣,點(diǎn)運(yùn)算符由表達(dá)式、點(diǎn)和名稱組成:

 . 

為了求解點(diǎn)表達(dá)式:

求出點(diǎn)左邊的,會(huì)產(chǎn)生點(diǎn)運(yùn)算符的對(duì)象。

會(huì)和對(duì)象的實(shí)例屬性匹配;如果該名稱的屬性存在,會(huì)返回它的值。

如果不存在于實(shí)例屬性,那么會(huì)在類中查找,這會(huì)產(chǎn)生類的屬性值。

這個(gè)值會(huì)被返回,如果它是個(gè)函數(shù),則會(huì)返回綁定方法。

在這個(gè)求值過程中,實(shí)例屬性在類的屬性之前查找,就像局部名稱具有高于全局的優(yōu)先級(jí)。定義在類中的方法,在求值過程的第三步綁定到了點(diǎn)運(yùn)算符的對(duì)象上。在類中查找名稱的過程有額外的差異,在我們引入類繼承的時(shí)候就會(huì)出現(xiàn)。

賦值。所有包含點(diǎn)運(yùn)算符的賦值語(yǔ)句都會(huì)作用于右邊的對(duì)象。如果對(duì)象是個(gè)實(shí)例,那么賦值就會(huì)設(shè)置實(shí)例屬性。如果對(duì)象是個(gè)類,那么賦值會(huì)設(shè)置類屬性。作為這條規(guī)則的結(jié)果,對(duì)對(duì)象屬性的賦值不能影響類的屬性。下面的例子展示了這個(gè)區(qū)別。

如果我們向賬戶實(shí)例的具名屬性interest賦值,我們會(huì)創(chuàng)建屬性的新實(shí)例,它和現(xiàn)有的類屬性具有相同名稱。

>>> jim_account.interest = 0.08

這個(gè)屬性值會(huì)通過點(diǎn)運(yùn)算符返回:

>>> jim_account.interest
0.08

但是,類屬性interest會(huì)保持為原始值,它可以通過所有其他賬戶返回。

>>> tom_account.interest
0.04

類屬性interest的改動(dòng)會(huì)影響tom_account,但是jim_account的實(shí)例屬性不受影響。

>>> Account.interest = 0.05  # changing the class attribute
>>> tom_account.interest     # changes instances without like-named instance attributes
0.05
>>> jim_account.interest     # but the existing instance attribute is unaffected
0.08
2.5.5 繼承

在使用 OOP 范式時(shí),我們通常會(huì)發(fā)現(xiàn),不同的抽象數(shù)據(jù)結(jié)構(gòu)是相關(guān)的。特別是,我們發(fā)現(xiàn)相似的類在特化的程度上有區(qū)別。兩個(gè)類可能擁有相似的屬性,但是一個(gè)表示另一個(gè)的特殊情況。

例如,我們可能希望實(shí)現(xiàn)一個(gè)活期賬戶,它不同于標(biāo)準(zhǔn)的賬戶。活期賬戶對(duì)每筆取款都收取額外的 $1,并且具有較低的利率。這里,我們演示上述行為:

>>> ch = CheckingAccount("Tom")
>>> ch.interest     # Lower interest rate for checking accounts
0.01
>>> ch.deposit(20)  # Deposits are the same
20
>>> ch.withdraw(5)  # withdrawals decrease balance by an extra charge
14

CheckingAccountAccount的特化。在 OOP 的術(shù)語(yǔ)中,通用的賬戶會(huì)作為CheckingAccount的基類,而CheckingAccountAccount的子類(術(shù)語(yǔ)“父類”和“超類”通常等同于“基類”,而“派生類”通常等同于“子類”)。

子類繼承了基類的屬性,但是可能覆蓋特定屬性,包括特定的方法。使用繼承,我們只需要關(guān)注基類和子類之間有什么不同。任何我們?cè)谧宇愇粗付ǖ臇|西會(huì)自動(dòng)假設(shè)和基類中相同。

繼承也在對(duì)象隱喻中有重要作用,不僅僅是一種實(shí)用的組織方式。繼承意味著在類之間表達(dá)“is-a”關(guān)系,它和“has-a”關(guān)系相反。活期賬戶是(is-a)一種特殊類型的賬戶,所以讓CheckingAccount繼承Account是繼承的合理使用。另一方面,銀行擁有(has-a)所管理的銀行賬戶的列表,所以二者都不應(yīng)繼承另一個(gè)。反之,賬戶對(duì)象的列表應(yīng)該自然地表現(xiàn)為銀行賬戶的實(shí)例屬性。

2.5.6 使用繼承

我們通過將基類放置到類名稱后面的圓括號(hào)內(nèi)來(lái)指定繼承。首先,我們提供Account類的完整實(shí)現(xiàn),也包含類和方法的文檔字符串。

>>> class Account(object):
        """A bank account that has a non-negative balance."""
        interest = 0.02
        def __init__(self, account_holder):
            self.balance = 0
            self.holder = account_holder
        def deposit(self, amount):
            """Increase the account balance by amount and return the new balance."""
            self.balance = self.balance + amount
            return self.balance
        def withdraw(self, amount):
            """Decrease the account balance by amount and return the new balance."""
            if amount > self.balance:
                return "Insufficient funds"
            self.balance = self.balance - amount
            return self.balance

CheckingAccount的完整實(shí)現(xiàn)在下面:

>>> class CheckingAccount(Account):
        """A bank account that charges for withdrawals."""
        withdraw_charge = 1
        interest = 0.01
        def withdraw(self, amount):
            return Account.withdraw(self, amount + self.withdraw_charge)

這里,我們引入了類屬性withdraw_charge,它特定于CheckingAccount類。我們將一個(gè)更低的值賦給interest屬性。我們也定義了新的withdraw方法來(lái)覆蓋定義在Account對(duì)象中的行為。類語(yǔ)句組中沒有更多的語(yǔ)句,所有其它行為都從基類Account中繼承。

>>> checking = CheckingAccount("Sam")
>>> checking.deposit(10)
10
>>> checking.withdraw(5)
4
>>> checking.interest
0.01

checking.deposit表達(dá)式是用于存款的綁定方法,它定義在Account類中,當(dāng) Python 解析點(diǎn)表達(dá)式中的名稱時(shí),實(shí)例上并沒有這個(gè)屬性,它會(huì)在類中查找該名稱。實(shí)際上,在類中“查找名稱”的行為會(huì)在原始對(duì)象的類的繼承鏈中的每個(gè)基類中查找。我們可以遞歸定義這個(gè)過程,為了在類中查找名稱:

如果類中有帶有這個(gè)名稱的屬性,返回屬性值。

否則,如果有基類的話,在基類中查找該名稱。

deposit中,Python 會(huì)首先在實(shí)例中查找名稱,之后在CheckingAccount類中。最后,它會(huì)在Account中查找,這里是deposit定義的地方。根據(jù)我們對(duì)點(diǎn)運(yùn)算符的求值規(guī)則,由于deposit是在checking實(shí)例的類中查找到的函數(shù),點(diǎn)運(yùn)算符求值為綁定方法。這個(gè)方法以參數(shù)10調(diào)用,這會(huì)以綁定到checking對(duì)象的self和綁定到10amount調(diào)用deposit方法。

對(duì)象的類會(huì)始終保持不變。即使deposit方法在Account類中找到,deposit以綁定到CheckingAccount實(shí)例的self調(diào)用,而不是Account的實(shí)例。

譯者注:CheckingAccount的實(shí)例也是Account的實(shí)例,這個(gè)說(shuō)法是有問題的。

調(diào)用祖先。被覆蓋的屬性仍然可以通過類對(duì)象來(lái)訪問。例如,我們可以通過以包含withdraw_charge的參數(shù)調(diào)用Accountwithdraw方法,來(lái)實(shí)現(xiàn)CheckingAccountwithdraw方法。

要注意我們調(diào)用self.withdraw_charge而不是等價(jià)的CheckingAccount.withdraw_charge。前者的好處就是繼承自CheckingAccount的類可能會(huì)覆蓋支取費(fèi)用。如果是這樣的話,我們希望我們的withdraw實(shí)現(xiàn)使用新的值而不是舊的值。

2.5.7 多重繼承

Python 支持子類從多個(gè)基類繼承屬性的概念,這是一種叫做多重繼承的語(yǔ)言特性。

假設(shè)我們從Account繼承了SavingsAccount,每次存錢的時(shí)候向客戶收取一筆小費(fèi)用。

>>> class SavingsAccount(Account):
        deposit_charge = 2
        def deposit(self, amount):
            return Account.deposit(self, amount - self.deposit_charge)

之后,一個(gè)聰明的總經(jīng)理設(shè)想了AsSeenOnTVAccount,它擁有CheckingAccountSavingsAccount的最佳特性:支取和存入的費(fèi)用,以及較低的利率。它將儲(chǔ)蓄賬戶和活期存款賬戶合二為一!“如果我們構(gòu)建了它”,總經(jīng)理解釋道,“一些人會(huì)注冊(cè)并支付所有這些費(fèi)用。甚至我們會(huì)給他們一美元。”

>>> class AsSeenOnTVAccount(CheckingAccount, SavingsAccount):
        def __init__(self, account_holder):
            self.holder = account_holder
            self.balance = 1           # A free dollar!

實(shí)際上,這個(gè)實(shí)現(xiàn)就完整了。存款和取款都需要費(fèi)用,使用了定義在CheckingAccountSavingsAccount中的相應(yīng)函數(shù)。

>>> such_a_deal = AsSeenOnTVAccount("John")
>>> such_a_deal.balance
1
>>> such_a_deal.deposit(20)            # $2 fee from SavingsAccount.deposit
19
>>> such_a_deal.withdraw(5)            # $1 fee from CheckingAccount.withdraw
13

就像預(yù)期那樣,沒有歧義的引用會(huì)正確解析:

>>> such_a_deal.deposit_charge
2
>>> such_a_deal.withdraw_charge
1

但是如果引用有歧義呢,比如withdraw方法的引用,它定義在AccountCheckingAccount中?下面的圖展示了AsSeenOnTVAccount類的繼承圖。每個(gè)箭頭都從子類指向基類。

對(duì)于像這樣的簡(jiǎn)單“菱形”,Python 從左到右解析名稱,之后向上。這個(gè)例子中,Python 按下列順序檢查名稱,直到找到了具有該名稱的屬性:

AsSeenOnTVAccount, CheckingAccount, SavingsAccount, Account, object

繼承順序的問題沒有正確的解法,因?yàn)槲覀兛赡軙?huì)給某個(gè)派生類高于其他類的優(yōu)先級(jí)。但是,任何支持多重繼承的編程語(yǔ)言必須始終選擇同一個(gè)順序,便于語(yǔ)言的用戶預(yù)測(cè)程序的行為。

擴(kuò)展閱讀。Python 使用一種叫做 C3 Method Resolution Ordering 的遞歸算法來(lái)解析名稱。任何類的方法解析順序都使用所有類上的mro方法來(lái)查詢。

>>> [c.__name__ for c in AsSeenOnTVAccount.mro()]
["AsSeenOnTVAccount", "CheckingAccount", "SavingsAccount", "Account", "object"]

這個(gè)用于查詢方法解析順序的算法并不是這門課的主題,但是 Python 的原作者使用一篇原文章的引用來(lái)描述它。

2.5.8 對(duì)象的作用

Python 對(duì)象系統(tǒng)為使數(shù)據(jù)抽象和消息傳遞更加便捷和靈活而設(shè)計(jì)。類、方法、繼承和點(diǎn)運(yùn)算符的特化語(yǔ)法都可以讓我們?cè)诔绦蛑行纬蓪?duì)象隱喻,它能夠提升我們組織大型程序的能力。

特別是,我們希望我們的對(duì)象系統(tǒng)在不同層面上促進(jìn)關(guān)注分離。每個(gè)程序中的對(duì)象都封裝和管理程序狀態(tài)的一部分,每個(gè)類語(yǔ)句都定義了一些函數(shù),它們實(shí)現(xiàn)了程序總體邏輯的一部分。抽象界限強(qiáng)制了大型程序不同層面之間的邊界。

面向?qū)ο缶幊踢m合于對(duì)系統(tǒng)建模,這些系統(tǒng)擁有相互分離并交互的部分。例如,不同用戶在社交網(wǎng)絡(luò)中互動(dòng),不同角色在游戲中互動(dòng),以及不同圖形在物理模擬中互動(dòng)。在表現(xiàn)這種系統(tǒng)的時(shí)候,程序中的對(duì)象通常自然地映射為被建模系統(tǒng)中的對(duì)象,類用于表現(xiàn)它們的類型和關(guān)系。

另一方面,類可能不會(huì)提供用于實(shí)現(xiàn)特定的抽象的最佳機(jī)制。函數(shù)式抽象提供了更加自然的隱喻,用于表現(xiàn)輸入和輸出的關(guān)系。一個(gè)人不應(yīng)該強(qiáng)迫自己把程序中的每個(gè)細(xì)微的邏輯都塞到類里面,尤其是當(dāng)定義獨(dú)立函數(shù)來(lái)操作數(shù)據(jù)變得十分自然的時(shí)候。函數(shù)也強(qiáng)制了關(guān)注分離。

類似 Python 的多范式語(yǔ)言允許程序員為合適的問題匹配合適的范式。為了簡(jiǎn)化程序,或使程序模塊化,確定何時(shí)引入新的類,而不是新的函數(shù),是軟件工程中的重要設(shè)計(jì)技巧,這需要仔細(xì)關(guān)注。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/38139.html

相關(guān)文章

  • SICP Python 描述 第二章 使用對(duì)象構(gòu)建抽象 2.1 引言

    摘要:對(duì)象表示信息,但是同時(shí)和它們所表示的抽象概念行為一致。通過綁定行為和信息,對(duì)象提供了可靠獨(dú)立的日期抽象。名稱來(lái)源于實(shí)數(shù)在中表示的方式浮點(diǎn)表示。另一方面,對(duì)象可以表示很大范圍內(nèi)的分?jǐn)?shù),但是不能表示所有有理數(shù)。 2.1 引言 來(lái)源:2.1 Introduction 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 在第一章中,我們專注于計(jì)算過程,以及程序設(shè)計(jì)中函數(shù)的作用。我們看到了...

    phoenixsky 評(píng)論0 收藏0
  • SICP Python 描述 2.6 實(shí)現(xiàn)類和對(duì)象

    摘要:以這種方式實(shí)現(xiàn)對(duì)象系統(tǒng)的目的是展示使用對(duì)象隱喻并不需要特殊的編程語(yǔ)言。我們的實(shí)現(xiàn)并不遵循類型系統(tǒng)的明確規(guī)定。反之,它為實(shí)現(xiàn)對(duì)象隱喻的核心功能而設(shè)計(jì)。是分發(fā)字典,它響應(yīng)消息和。 2.6 實(shí)現(xiàn)類和對(duì)象 來(lái)源:2.6 Implementing Classes and Objects 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 在使用面向?qū)ο缶幊谭妒綍r(shí),我們使用對(duì)象隱喻來(lái)指導(dǎo)程序...

    chenjiang3 評(píng)論0 收藏0
  • SICP Python 描述 第三章 計(jì)算機(jī)程序的構(gòu)造和解釋 3.1 引言

    摘要:為通用語(yǔ)言設(shè)計(jì)解釋器的想法可能令人畏懼。但是,典型的解釋器擁有簡(jiǎn)潔的通用結(jié)構(gòu)兩個(gè)可變的遞歸函數(shù),第一個(gè)求解環(huán)境中的表達(dá)式,第二個(gè)在參數(shù)上調(diào)用函數(shù)。這一章接下來(lái)的兩節(jié)專注于遞歸函數(shù)和數(shù)據(jù)結(jié)構(gòu),它們是理解解釋器設(shè)計(jì)的基礎(chǔ)。 3.1 引言 來(lái)源:3.1 Introduction 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 第一章和第二章描述了編程的兩個(gè)基本元素:數(shù)據(jù)和函數(shù)之間的...

    v1 評(píng)論0 收藏0
  • ApacheCN 編程/大數(shù)據(jù)/數(shù)據(jù)科學(xué)/人工智能學(xué)習(xí)資源 2019.4

    摘要:我們是一個(gè)大型開源社區(qū),旗下群共余人,數(shù)量超過個(gè),網(wǎng)站日超過,擁有博客專家和簡(jiǎn)書程序員優(yōu)秀作者認(rèn)證。我們組織公益性的翻譯活動(dòng)學(xué)習(xí)活動(dòng)和比賽組隊(duì)活動(dòng),并和等國(guó)內(nèi)著名開源組織保持良好的合作關(guān)系。 Special Sponsors showImg(https://segmentfault.com/img/remote/1460000018907426?w=1760&h=200); 我們是一個(gè)...

    tomorrowwu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<