零基礎學Python--------第7章 面向對象程序設計


第7章 面向對象程序設計

7.1 面向對象概述

面向對象(Object Oriented)的英文縮寫是OO,它是一種設計思想。從20世紀60年代提出面向對象的概念到現在,它已經發展成為一種比較成熟的編輯思想,並且逐步成為目前軟件開發領域的主流計技術。如我們經常聽說的面向對象編程(Object Oriented Programming,即OOP)就是主要針對大型軟件設計而提出的,它可以使軟件設計更加靈活,並且能更好地進行代碼復用。

面向對象中的對象(Object),通常是指客觀世界中存在的對象,具有唯一性,對象之間各不相同,各有各的特點,么一個對象都有自己的運動規律和內部狀態;對象與對象之間有時可以相互聯系、相互作用。另外,對象也可以是一個抽象的事物,例如,可以從圓形、正方形、三角形等圖形抽象出一個簡單圖形,簡單圖就是一個對象,它有自己的屬性和行為,圖形中邊的個數是它的屬性,圖形的面積也是它的屬性,輸出圖形的面積就是它的行為。概括地講,面向對象技術是一種從組織結構上模擬客觀世界的方法。

7.1.1 對象

對象,是一個抽象概念,英文稱作“Object”,表示任意存在的事物。世間萬物皆對象!現實世界中,隨處可見的一種事物就是對象,對象是事物存在的實體,如一個人。

通常將對象划分為兩個部分,即靜態部分和動態部分。靜態部分被稱為“屬性”,任何對象都具備自身屬性,這些屬性不僅是客觀存在的,而且是不能被忽視的,如人的性別。動態部分指的是對象的行為,即對象執行的動作,如人可以跑步。

說明:在Python中,一切都是對象。即不僅是具體的事物稱為對象,字符串、函數等也都是對象。這說明Python天生就是面向對象的。

7.1.2 類

類是封裝對象的屬性和行為的載體,反過來說具有相同屬性和行為的一類實體被稱為類。例如,把雁群比作大雁類,那么大雁類就具備了喙、翅膀和爪等屬性,覓食、飛行和睡覺等行為,而一只要從北方飛往南方的大雁被視為大雁類的一個對象。大雁類和大雁對象的關系。

在Python語言中,類是一種抽象概念,如定義一個大雁類(Geese),在該類中,可以定義每個對象共有的屬性和方法;而一只要從北方飛往南方的大雁則是大雁類的一個對象(wildGeese),對象是類的實例。有關類的具體實現將在7.2節進行詳細介紹。

7.1.3 面向對象程序設計的特點

 面向對象程序設計具有三大基本特征:封裝、繼承和多態。

1. 封裝

封裝是面向對象編程的核心思想,將對象的屬性和行為封裝起來,其載體就是類,類通常會對客戶隱藏其現實細節,這就是封裝的思想。例如,用戶使用計算機,只需要使用手機敲擊鍵盤就可以實現一些功能,而不需要知道計算機內部是如何工作的。

采用封裝思想保證了類內部數據結構的完整行,使用該類的用戶不能這看到類中的數據結構,而只能執行類允許公開的數據,這樣就避免了外部對內部數據的影響,提高了程序的可維護性。

2. 繼承

矩形、菱形、平行四邊形和梯形等都是四邊形。因為四邊形與它們具有共同的特征:擁有4條邊。只要將四邊矩形適當延伸,就會得到矩形、菱形、平行四邊形和梯形4 種圖形。以平行四邊形為例,如果把平行四邊形的延伸,那么平行四邊形就復用了四邊形的屬性和行為,同時添加了平行四邊形特有的屬性和行為,如平行四邊形的對邊平行且相等。在Python中,可以把平行四邊形類看作是繼承四邊形類后產生的類,其中,將類似於平行四邊形的類稱為子類,將類似於四邊形的類稱為父類和超類。值得注意的是,在闡述平行四邊形和四邊形的關系時,可以說平行四邊形是特殊的四邊形,但不能說四邊形是平行四邊形。同理,Python 中可以說子類的實例都是父類的實例,但不能說父類的實例是子類的實例。

綜上所述,繼承是實現重復利用的重要手段,子類通過繼承復用了父類的屬性和行為的同時又添加了子類特有的屬性和行為。

3. 多態

將父類對象應用於子類的特征就是多態。比如創建一個螺絲類,螺絲類有兩個屬性:粗細和螺紋密度;然后在創建了兩個類,一個是長螺絲類,一個短螺絲類,並且它們都繼承了螺絲類。這樣長螺絲類和短螺絲類不僅具有相同的特征(粗細形同,且螺紋密度也相同),還具有不同的特征(一個長,一個短,長的可以用來固定大型支架,短的可以固定生活中的家具)。綜上所述,一個螺絲類衍生出不同的子類,子類繼承父類特征的同時,也具備了自己的特征,並且能夠實現不同的效果,這就是多態化的結構。

7.2 類的定義和使用

在Python 中,類表示具有相同屬性和方法的對象的集合。在使用類時,需要先定義類,然后再創建類的實例,通過類的實例就可以訪問類中的屬性和方法了。

7.2.1 定義類

在Python 中,類的定義使用class 關鍵字來實現,語法如下:

class ClassName:
    '''類的幫助信息'''             # 類文檔字符串
    statement                    # 類體

 

參數說明:

  • ClassName:用於指定類名,一般使用大寫字母開頭,如果類名中包括兩個單詞,第二個單詞的首字母也大寫,這種命名方法也稱為“駝峰式命名法”,這是慣例。當然也可以根據自己的習慣命名,但是一般推薦按照慣例來命名。
  • '''類的幫助信息''':用於指定類的文檔字符串,定義該字符串后,在創建類的對象是,輸入類名和左側的括號“(” 后,將顯示該信息。
  • statement:類體,主要由類變量(或類成員)、方法和屬性等定義語句組成。如果在定義類時,沒想好類的具體功能,可可以在類體中直接使用pass 語句代替。

例如,下面以大雁為例聲明一個類,代碼如下:

class Geese:
    '''大雁類'''
    pass

 

7.2.2 創建類的實例

定義完類后,並不會真正的創建一個實例。這有點像一個汽車的設計圖。設計圖可以告訴你汽車看上去怎樣,但設計圖本身不是一個汽車。你不能開走它,它只能用來創造真正的汽車,而且可以使用它制造很多汽車。那么如何創建實例呢?

class語句本身不創建該類的任何實例。所以在類定義完成以后,可以創建類的實例,即實例化該類的對象。創建類的實例的語法如下:

ClassName(parameterlist)

 

其中,ClassName 是必選參數,用於指定具體的類;parameterlist 是可選參數,當創建一個類時,沒有創建__init__()方法(該方法將在7.2.3 小節進行詳細介紹),或者__init__() 方法只有一個self 參數時,parameterlist 可以省略。

class Geese:
    '''大雁類'''
    pass
wildGoose = Geese()             # 創建大雁類的實例
print(wildGoose)

 

執行上面的代碼后,將顯示類似下面的內容:

<__main__.Geese object at 0x028BD430>

 

從上面的執行結果中可以看出,wildGoose是Geese類的實例。

7.2.3 創建__init__() 方法

在創建類后,可以手動創建一個__init__() 方法。該方法是一個特殊的方法,類似Java 語言中的構造方法。每當創建一個類的新實例時,Python 都會自動執行它。__init__() 方法必須包含一個self 參數,並且必須是一個第一個參數。self 參數是一個指向實例本身的引用,用於訪問類中的屬性和方法。在方法調用時會自動傳遞實際參數self,因此當__init__() 方法只有一個參數時,在創建類的實例時,就不需要指定實際參數了。

說明:在__init__() 方法的名稱中,開頭和結尾處是兩個下划線(中間沒有空格),這是一種約定,旨在區分Python 默認方法和普通方法。

例如,下面仍然以大雁為例聲明一個類,並且創建__init__() 方法,代碼如下:

class Geese:
    '''大雁類'''
    def __init__(self):         #構造方法
        print('我是大雁類!')
wildGoose = Geese()             # 創建大雁類的實例

運行上面的代碼,將輸出以下內容:

我是大雁類!

 

從上面的運行結果可以看出,在創建大雁類的實例時,雖然沒有為__init__() 方法指定參數,但是該方法會自動執行。

在__init__() 方法中,除了self 參數外,還可以自定義一些參數,參數間使用逗號“,” 進行分隔。

例如,下面的代碼將在創建__init__()方法時,在指定3個參數,分別是beak、wing和claw。

lass Geese:
    '''大雁類'''
    def __init__(self,beak,wing,claw):        #構造方法
        print('我是大雁類!我有以下特征:')
        print(beak)             # 輸出喙的特征
        print(wing)             # 輸出翅膀的特征
        print(claw)             # 輸出爪子的特征
beak_1 = "喙的基本較高,長度和頭部的長度幾乎相等"  # 喙的特征
wing_1 = "翅膀長而尖"           # 翅膀的特征
claw_1 = "爪子是蹼狀的"         # 爪子的特征
wildGoose = Geese(beak_1,wing_1,claw_1)             # 創建大雁類的實例

 

執行上面的代碼,結果如下。

我是大雁類!我有以下特征:
喙的基本較高,長度和頭部的長度幾乎相等
翅膀長而尖
爪子是蹼狀的

7.2.4 創建類的成員並訪問

類的成員主要有實例方法和數據成員組成。在類中創建了類的成員后,可以通過類的實例進行訪問。

1. 創建實例方法並訪問

所謂實例方法是指在類中定義的函數。該函數是一種在類的實例上操作的函數。同__init__() 方法一樣,實例方法的第一個參數必須是self,並且必須包含一個self 參數。創建實例方法的語法格式如下:

def functionName(self,parameterlist):
    block

 

參數說明:

  • functionName:用於指定方法名,一般使用小寫字母開頭。
  • self:必要參數,表示類的實例,其名稱可以是self 以外的單詞,使用self 只是一個慣例而已。
  • parameterlist:用於指定出self 參數以外的參數,各參數間使用逗號“,” 進行分隔。
  • block:方法體,顯示的具體功能。

說明:實例方法和Python 中的函數的主要區別就是,函數實現的是某個獨立的功能,而實例方法是顯示類中的一個行為,是類的一部分。

 實例方法創建完成后,可以通過類的實例名稱和點(.)操作符進行訪問,語法格式如下:

intanceName.functionName(parametervalue)

 

參數說明:

  • instanceName:為類的實例名稱。
  • functionName:為要調用的方法名稱。
  • parametervalue:表示為方法指定對應的實際參數,其值的個數與創建實例方法中parameterlist的個數相同。

實例01:創建大雁並定義飛行方法

class Geese:
    '''大雁類'''
    def __init__(self,beak,wing,claw):        #構造方法
        print('我是大雁類!我有以下特征:')
        print(beak)             # 輸出喙的特征
        print(wing)             # 輸出翅膀的特征
        print(claw)             # 輸出爪子的特征
    def fly(self,state):        # 定義飛行方法
        print(state)
'''****************調用方法***************'''    
beak_1 = "喙的基本較高,長度和頭部的長度幾乎相等"  # 喙的特征
wing_1 = "翅膀長而尖"           # 翅膀的特征
claw_1 = "爪子是蹼狀的"         # 爪子的特征
wildGoose = Geese(beak_1,wing_1,claw_1)             # 創建大雁類的實例
wildGoose.fly("我飛行的時候,一會兒排成個人字,一會兒排成個一字")  # 調用實例方法

 

運行結果

我是大雁類!我有以下特征:
喙的基本較高,長度和頭部的長度幾乎相等
翅膀長而尖
爪子是蹼狀的
我飛行的時候,一會兒排成個人字,一會兒排成個一字

 

多學兩招:在創建實例方法時,也可以和創建函數是一樣為參數設置默認值。但是被設置了默認值的參數必須位於所有參數的最后(即最右側)。例如,可以將實例01的第8行代碼修改為以下內容:

def fly(self,state = '我會飛行'):

 

2. 創建數據成員並訪問

數據成員是指在類中定義的變量,即屬性,根據定義位置,又可以分為類屬性和實例屬性。

  • 類屬性

類屬性是指定義在類中,並且在函數體外的屬性。類屬性可以在類的所有實例之間共享值,也就是在所有實例化的對象中共用。

說明:類屬性可以通過類名稱或者實例名訪問。

……

  • 實例屬性

實例出行是指定義在類的方法中的屬性,只作用於當前實例中。

7.2.5 訪問限制

在類的內部可以定義屬性和方法,而在類的外部則可以直接調用屬性或方法來操作數據,從而隱藏了類內部的復雜邏輯。但是Python 並沒有對屬性和方法名前面添加單下划線(_foo)、雙下划線(__foo)或首尾加雙下划線(__foo__),從而限制訪問權限。其中,單下划線、雙下划線、首尾雙下划線的作用如下:

(1)首尾雙下划線表示定義特殊方法,一般是系統定義名字,如__init__()。

(2)以單下划線開頭的表示protected(保護)類型的成員,只允許本身和子類進行訪問,但不能使用“from module imort*”語句導入。

……

(3)雙下划線表示private(私有)類型成員,只允許定義該方法的類本身進行訪問,而且也不能通過類的實例進行訪問,但是可以通過“類的實例名._類名__xxx”方式訪問。

7.3 屬性(property)

本節介紹的屬性與7.2.4小節介紹的類屬性和實例屬性不同。7.2.4小節介紹的屬性將返回存儲的值,而本節介紹的屬性則是一種特殊的屬性,訪問它時將計算它的值。另外,該屬性還可以為屬性添加安全保護機制。

7.3.1 創建用於計算的屬性

在Python中,可以通過@property(裝飾器)將一個方法轉換為屬性,從而實現用於計算的屬性。將方法轉換為屬性后,可以直接通過方法名來訪問方法,而不需要再添加一對小括號“o”,這樣可以讓代碼更加簡潔。

通過@property創建用於計算的屬性的語法格式如下:

@property
def methodname(self):
    block

 

參數說明:

  • methodname:用於指定方法名,一般使用小寫字母開頭。該名稱最后將作為創建的屬性名。
  • self:必要參數,表示類的實例。
  • block:方法體,實現的具體功能。在方法體中,通常以return語句結束,用於返回計算結果。

……

注意:通過@property轉換后的屬性不能重復賦值,如果對其重新賦值,將拋出異常信息。

7.3.2 為屬性添加安全保護機制

在Python中,默認情況下,創建的類屬性或者實例是可以在類 體外進行修改的,如果想要限制其不能在類體外修改,可以將其設置為私有的,但設置為私有后,在類體外也不能獲取它的值。如果想要創建一個可以讀取但不能修改的屬性,那么可以使用@property實現只讀屬性。

……

通過屬性不僅可以將屬性設置為只讀屬性,而且可以為屬性設置攔截器,即允許對屬性進行修改,但修改時需要遵守一定的約束。

7.4 繼承

在編寫類時,並不是每次都要從空白開始。當要編寫的類和另一個已經存在的類之間存在一定的繼承關系時,就可以通過繼承來達到代碼重用的目的,提高開發效率。下面介紹如果在Python中實現繼承。

7.4.1 繼承的基本語法

繼承是面向對象編程最重要的特性之一,它源於人們認識客觀世界的過程,是自然界普遍存在的一種現象。例如,我們每一個人都從祖輩和父母那里繼承了一些體貌特征,但是每個人卻又不同與父母,因為每個人都存在自己的一些特性,這些特性是獨有的,在父母身上並沒有體現。在程序設計中實現繼承,表示這個類擁有它繼承的類的素有公有成員或者受保護成員。在面向對象編程中,被繼承的類稱為父類或基類,新的類稱為子類或派生類。

通過繼承不僅可以實現代碼的重用,還可以通過繼承來順類與類之間的關系。在Python中,可以在類定義語句中,類名右側使用一對小括號將要繼承的類名稱括起來,從而實現類的繼承。

……

參數說明:

  • ClassName:用於指定類名。
  • baseclasslist:用於指定要繼承的基類,可以有多個,類名之間用逗號“,”分隔。如果不指定,將使所有Python對象的根類object。
  • '''類的幫助信息''':用於指定類的文檔字符串,定義該字符串后,在創建類的對象時,輸入類名和左側的括號“(”后,將顯示信息。
  • statement:類體,主要由類變量(或類成員)、方法和屬性等定義語句組成。如果在定義類時,沒想好類的具體功能,也可以在類體中直接使用pass語句代替。

……

7.4.2 方法重寫

基類的成員都會被派生類繼承,當基類中的某個方法不完全使用與派生類時,就需要在派生類中重寫父類的這個方法,這個和Java語言中的方法重寫是一樣的。

7.4.3 派生類中調用基類的__init__()方法

在派生類中定義__init__()方法時,不會自動調用基類的__init__()方法。例如,定義一個Fruit類,在__init__()方法中創建類屬性color,然后在Fruit類中定義一個harvest()方法。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM