1. 程式人生 > >python基礎八_面向物件程式設計

python基礎八_面向物件程式設計

一.面向物件程式設計——Object Oriented Programming,簡稱OOP,是一種程式設計思想。OOP把物件作為程式的基本單元,一個物件包含了資料和操作資料的函式。
面向過程的程式設計把計算機程式視為一系列的命令集合,即一組函式的順序執行。為了簡化程式設計,面向過程把函式繼續切分為子函式,即把大塊函式通過切割成小塊函式來降低系統的複雜度。
我們以一個例子來說明面向過程和麵向物件在程式流程上的不同之處。

假設我們要處理學生的成績表,為了表示一個學生的成績,面向過程的程式可以用一個dict表示:

std1 = { 'name': 'Michael', 'score': 98 }
std2 = { 'name': 'Bob', 'score': 81 }

而處理學生成績可以通過函式實現,比如列印學生的成績:

def print_score(std):
    print('%s: %s' % (std['name'], std['score']))

如果採用面向物件的程式設計思想,我們首選思考的不是程式的執行流程,而是Student這種資料型別應該被視為一個物件,這個物件擁有name和score這兩個屬性(Property)。如果要列印一個學生的成績,首先必須創建出這個學生對應的物件,然後,給物件發一個print_score訊息,讓物件自己把自己的資料打印出來。

class Student(object):

    def __init__(self, name, score):
        self.name = name
        self.score = score

    def print_score(self):
        print('%s: %s' % (self.name, self.score))

給物件發訊息實際上就是呼叫物件對應的關聯函式,我們稱之為物件的方法(Method)。面向物件的程式寫出來就像這樣:

bart = Student('Bart Simpson', 59)
lisa = Student('Lisa Simpson', 87)
bart.print_score()
lisa.print_score()

面向物件的設計思想是從自然界中來的,因為在自然界中,類(Class)和例項(Instance)的概念是很自然的。Class是一種抽象概念,比如我們定義的Class——Student,是指學生這個概念,而例項(Instance)則是一個個具體的Student,比如,Bart Simpson和Lisa Simpson是兩個具體的Student。

所以,面向物件的設計思想是抽象出Class,根據Class建立Instance。

面向物件的抽象程度又比函式要高,因為一個Class既包含資料,又包含操作資料的方法。

資料封裝、繼承和多型是面向物件的三大特點

二.類與例項

由於類可以起到模板的作用,因此,可以在建立例項的時候,把一些我們認為必須繫結的屬性強制填寫進去。通過定義一個特殊的__init__方法,在建立例項的時候,就把namescore等屬性綁上去:

class Student(object):

    def __init__(self, name, score):
        self.name = name
        self.score = score

注意到__init__方法的第一個引數永遠是self,表示建立的例項本身,因此,在__init__方法內部,就可以把各種屬性繫結到self

,因為self就指向建立的例項本身。

有了__init__方法,在建立例項的時候,就不能傳入空的引數了,必須傳入與__init__方法匹配的引數,但self不需要傳,Python直譯器自己會把例項變數傳進去:

>>> bart = Student('Bart Simpson', 59)
>>> bart.name
'Bart Simpson'
>>> bart.score
59

和普通的函式相比,在類中定義的函式只有一點不同,就是第一個引數永遠是例項變數self,並且,呼叫時,不用傳遞該引數。除此之外,類的方法和普通函式沒有什麼區別,所以,你仍然可以用預設引數、可變引數、關鍵字引數和命名關鍵字引數。

方法就是與例項繫結的函式,和普通函式不同,方法可以直接訪問例項的資料;

三.資料封裝

class中封裝有資料變數與操作變數的方法

如果要讓內部屬性不被外部訪問,可以把屬性的名稱前加上兩個下劃線__,在Python中,例項的變數名如果以__開頭,就變成了一個私有變數(private),只有內部可以訪問,外部不能訪問,所以,我們把Student類改一改:

class Student(object):

    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

改完後,對於外部程式碼來說,沒什麼變動,但是已經無法從外部訪問例項變數.__name例項變數.__score


如果想要獲得與修改私有化的變數,自己寫get.set方法

 def get_name(self):
        return self.__name
def set_score(self, score):
        self.__score = score

通過set方法修改值與直接修改值的區別:可以防止不正確的值傳入

def set_score(self, score):
        if 0 <= score <= 100:
            self.__score = score
        else:
            raise ValueError('bad score')

需要注意的是,在Python中,變數名類似__xxx__的,也就是以雙下劃線開頭,並且以雙下劃線結尾的,是特殊變數,特殊變數是可以直接訪問的,不是private變數,所以,不能用__name____score__這樣的變數名。

有些時候,你會看到以一個下劃線開頭的例項變數名,比如_name,這樣的例項變數外部是可以訪問的,但是,按照約定俗成的規定,當你看到這樣的變數時,意思就是,“雖然我可以被訪問,但是,請把我視為私有變數,不要隨意訪問”。

雙下劃線開頭的例項變數是不是一定不能從外部訪問呢?其實也不是。不能直接訪問__name是因為Python直譯器對外把__name變數改成了_Student__name,所以,仍然可以通過_Student__name來訪問__name變數:

>>> bart._Student__name
'Bart Simpson'

但是強烈建議你不要這麼幹,因為不同版本的Python直譯器可能會把__name改成不同的變數名。

總的來說就是,Python本身沒有任何機制阻止你幹壞事,一切全靠自覺。

最後注意下面的這種錯誤寫法:

>>> bart = Student('Bart Simpson', 98)
>>> bart.get_name()
'Bart Simpson'
>>> bart.__name = 'New Name' # 設定__name變數!
>>> bart.__name
'New Name'

表面上看,外部程式碼“成功”地設定了__name變數,但實際上這個__name變數和class內部的__name變數不是一個變數!內部的__name變數已經被Python直譯器自動改成了_Student__name,而外部程式碼給bart新增了一個__name變數:

>>> bart.get_name() # get_name()內部返回self.__name
'Bart Simpson'

四.繼承與多型

例:

class Dog(Animal)

繼承好處?

1是子類獲得了父類的全部功能。

2可以對父類的方法進行重寫(先有繼承後有重寫,引數數量同父類中的方法相同,方法名相同,而過載方法名同,引數數量不同或型別不同)

在程式碼執行的時候,總是會呼叫子類的重寫後的方法