官术网_书友最值得收藏!

2.2.1 類

類(class)定義了如何構造對象以及它們所具有的特征和知識。有些人喜歡將類比作藍圖,它們都是對對象擁有的信息和功能的一般描述。對象和類相關但是不同。如果類是藍圖,那對象就是完工的建筑。

我們在Python中使用保留關鍵字class定義類。按照慣例,類名以大寫字母開頭,每個新單詞的開頭也都用大寫字母(此慣例被稱為Pascal case)。讓我們創建一個模擬咖啡機的類:

在上述代碼中,我們定義了一個表示咖啡機的類。我們可以使用這個類來生成新的咖啡機對象,這個過程稱為實例化(instantiation)。實例化一個類,是指創建該類的一個新的對象。通過調用類的名稱來實例化,就像它是一個返回實例化對象的函數一樣:

現在我們有了machine對象,其功能由CoffeeMachine類定義(它仍然是空的,我們將在后面的部分進行完善)。當類被實例化時,它的__init__函數將被調用。在__init__函數中,我們可以進行一些初始化操作。例如,這里我們添加了一個煮咖啡的數量,并將其設置為零:

注意__coffees_brewed開頭的兩個下劃線。如果你還記得之前關于訪問級別的討論,默認情況下,Python的所有數據都對外部可見。雙下劃線命名模式用于表示某物是私有的,不希望被直接訪問。

在本例中,我們不希望外界訪問__coffees_brewed;否則他們可以隨意改變咖啡沖泡次數!

如果不能訪問__coffees_brewed,那我們如何知道機器煮了多少杯咖啡呢?答案是特性。特性是類的只讀屬性。不過,在討論特性之前,還有一些語法需要介紹。

1. 變量self

如果查看前面的示例,你會發現我們經常使用一個名為self的變量。我們也可以使用其他名稱,不過約定使用self。正如你前面看到的,我們將其傳遞給類中的每個函數,包括初始化函數。多虧了self這第一個形參,我們才可以訪問類中定義的所有數據。例如,在__init__函數中,我們將變量__coffees_brewed加到self后面,這樣,這個變量就存在于對象中了。

變量self必須是類中每個函數定義的第一個形參,但是當我們在類的實例上調用這些函數時,它不需要作為第一個實參數被傳遞。例如,為了實例化CoffeeMachine類,我們寫了如下代碼:

調用初始化函數時沒有任何形參(沒有self)。如果你細想一下,如果我們還沒有初始化對象,怎么可能將該初始化函數作為self傳遞呢?原來,Python已經為我們解決了這個問題:我們永遠不需要將self傳遞給初始化函數或對象的任何方法或特性。

調用self正是指類的不同屬性訪問類中的其他定義的方式。例如,在我們稍后將編寫的brew_coffee方法中,我們正是使用self來訪問__coffees_brewed的數量:

理解self以后,我們就可以學習特性了。

2. 類的特性

對象的特性(property)是可以返回數據的只讀屬性。使用點號即可訪問對象的特性:object.property。還是以咖啡機為例,我們可以添加一個coffees_brewed特性(用咖啡機煮的咖啡數量),代碼如下:

然后,我們可以訪問它:

特性是使用@property裝飾器定義的函數:

特性不能接收參數(self除外),且需要有返回值。不返回任值或接收其他參數的特性在概念上就是錯誤的:特性應該是我們請求對象提供的只讀數據。

我們提到,@property是裝飾器的一個例子。Python裝飾器允許我們修改特性的行為。@property修改類的函數,以便它可以像類的屬性一樣被使用。本書不會再使用其他裝飾器,所以我們不會對此進行講解,但如果你有興趣,我鼓勵你自行研究。

特性告訴我們對象的信息。例如,如果我們想知道某個CoffeeMachine類的實例是否至少煮了一杯咖啡,我們可以添加如下特性:

現在就可以詢問CoffeeMachine類的實例,是否已經煮過咖啡了:

顯然,這臺機器還沒有準備好咖啡,那么如何讓CoffeeMachine的實例為我們煮咖啡呢?使用方法。

3. 類的方法

特性允許我們了解對象的某些信息:通過回答我們的詢問。為了讓對象執行一些任務,我們使用方法。方法(method)不過是屬于類的函數,可以訪問類中定義的屬性。在CoffeeMachine類的代碼中,編寫一個方法來請求它煮咖啡:

方法將self作為第一個形參,這使它們能夠訪問類中定義的所有內容。正如我們前面討論的,在調用對象的方法時,我們不需要傳遞self參數,Python會為我們代勞。

注意:特性類似于用@property裝飾的方法。特性和方法都將self作為它們的第一個實參。在調用方法時,我們使用括號并可選地傳遞其參數,但是訪問屬性時不需要使用括號。

我們可以在實例上調用brew_coffee方法:

既然第一杯咖啡已經煮好,我們可以詢問實例:

如你所見,方法必須在類(對象)的特定實例上調用。此對象將是響應請求的對象。函數的調用不需要對象,如下所示:

然而方法必須對對象進行調用,如下所示:

對象只能響應創建它們的類中定義的方法。如果在對象上調用了一個方法(或任何特性),但該方法在類中沒有定義,則會觸發一個屬性錯誤(AttributeError)。讓我們試一試。讓咖啡機泡一杯茶,盡管我們從來沒有告訴過它怎么泡茶:

好吧,對象“抱怨”說:我們從來沒有說過,希望它學會如何泡茶。以下“抱怨”的關鍵:

教訓是:永遠不要請求對象做它沒有學過的事情,這會嚇壞它,并讓你的程序失效。

方法可以接受任意數量的形參,但必須在第一個實參self之后定義。例如,讓我們在CoffeeMachine類中添加一個方法,讓我們能夠給咖啡機倒入給定數量的水:

我們可以通過調用這個新方法來給咖啡機實例加水:

在繼續學習其他知識之前,關于方法需要知道的最后一點是它們強大的動態調度特性。當在對象上調用方法時,Python將檢查該對象是否響應該方法,但是,關鍵點在于,只要該對象的類定義了所請求的方法,Python并不關心對象的類。

我們可以使用這個特性來定義響應相同方法的不同對象(相同的方法指的是相同的名稱和實參),并可以互換地使用它們。例如,我們可以定義一個新的現代咖啡生產商:

現在,我們可以編寫函數,期望有一個咖啡生產者(任何定義了brew_coffee()方法的類的對象),并對其執行某些操作:

這個函數對CoffeeMachine和CoffeeHipster的實例都適用:

為了達成這種效果,我們需要確保這些方法具有相同的“簽名”,也就是說,它們的名稱相同,形參也完全一致。

主站蜘蛛池模板: 潢川县| 利辛县| 九江县| 宁蒗| 涞源县| 青河县| 玛多县| 高平市| 股票| 都兰县| 庆城县| 九龙县| 武汉市| 金山区| 仲巴县| 诸城市| 大化| 沂南县| 偏关县| 甘孜县| 日照市| 朔州市| 威信县| 商水县| 潼关县| 无极县| 和龙市| 湖口县| 龙岩市| 北流市| 宝坻区| 上犹县| 错那县| 吴桥县| 绥中县| 乌拉特中旗| 库车县| 林甸县| 贺州市| 中牟县| 新巴尔虎右旗|