1. 程式人生 > >Python面向物件程式設計(類和例項 訪問限制 繼承和多型 獲取物件資訊 例項屬性和類屬性)

Python面向物件程式設計(類和例項 訪問限制 繼承和多型 獲取物件資訊 例項屬性和類屬性)

面向物件程式設計——Object Oriented Programming,簡稱OOP,是一種程式設計思想。OOP把物件作為程式的基本單元,一個物件包含了資料和操作資料的函式。

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

在Python中,所有資料型別都可以視為物件,當然也可以自定義物件。自定義的物件資料型別就是面向物件中的類(Class)的概念。

類和例項

類是抽象的模板,比如Student類,而例項是根據類創建出來的一個個具體的“物件”,每個物件都擁有相同的方法,但各自的資料可能不同。

  1. 類可以起到模板的作用,因此,可以在建立例項的時候,通過定義一個特殊的_ init _方法,把一些我們認為必須繫結的屬性強制填寫進去。

    注意:特殊方法“init”前後有兩個下劃線!!!

  2. _ init_ 方法的第一個引數永遠是self,表示建立的例項本身,因此,在init方法內部,就可以把各種屬性繫結到self,因為self就指向建立的例項本身。

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

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

  5. 封裝的另一個好處是可以給類增加新的方法。

  6. 總結:
    類是建立例項的模板,而例項則是一個一個具體的物件,各個例項擁有的資料都互相獨立,互不影響;
    方法就是與例項繫結的函式,和普通函式不同,方法可以直接訪問例項的資料;通過在例項上呼叫方法,我們就直接操作了物件內部的資料,但無需知道方法內部的實現細節。
    和靜態語言不同,Python允許對例項變數繫結任何資料,也就是說,對於兩個例項變數,雖然它們都是同一個類的不同例項,但擁有的變數名稱都可能不同。

#常見的類
class Student(object):
    def __init__(self,name,score): #繫結屬性
        self.name = name #外來引數賦值給self自己的引數
self.score = score def print_score(self): print('%s: %s' % (self.name,self.score)) def get_grade(self): if self.score >=90: return 'A' elif self.score >=60: return 'B' else: return 'C' bart = Student('Bart Simpson', 59) bart.print_score() print(bart.get_grade())

訪問限制

在Python中,例項的變數名如果以__開頭,就變成了一個私有變數(private),只有內部可以訪問,外部不能訪問。

外部要訪問私有程式碼要用get方法;要修改的話就用set方法。在方法中,可以對引數做檢查,避免傳入無效的引數。

繼承和多型

在OOP程式設計中,當我們定義一個class的時候,可以從某個現有的class繼承,新的class稱為子類(Subclass),而被繼承的class稱為基類、父類或超類(Base class、Super class)。

繼承可以把父類的所有功能都直接拿過來,這樣就不必重零做起,子類只需要新增自己特有的方法,也可以把父類不適合的方法覆蓋重寫。

動態語言的鴨子型別特點決定了繼承不像靜態語言那樣是必須的。

  1. “開閉”原則:
    對擴充套件開放:允許新增父類的子類;
    對修改封閉:不需要修改依賴父類型別的固有方法等函式。

靜態語言 vs 動態語言:
1. 對於靜態語言(例如Java)來說,如果需要傳入Animal型別,則傳入的物件必須是Animal型別或者它的子類,否則,將無法呼叫run()方法。

  1. 對於Python這樣的動態語言來說,則不一定需要傳入Animal型別。我們只需要保證傳入的物件有一個run()方法就可以了。

動態語言的“鴨子型別”,它並不要求嚴格的繼承體系,一個物件只要“看起來像鴨子,走起路來像鴨子”,那它就可以被看做是鴨子。

獲取物件資訊

當我們拿到一個物件的引用時,如何知道這個物件是什麼型別、有哪些方法呢?

  1. 使用type()函式:用來來判斷物件型別,它返回對應的Class型別。

  2. 要判斷一個物件是否是函式怎麼辦?可以使用types模組中定義的常量:

>>> import types
>>> def fn():
...     pass
...
>>> type(fn)==types.FunctionType
True
>>> type(abs)==types.BuiltinFunctionType
True
>>> type(lambda x: x)==types.LambdaType
True
>>> type((x for x in range(10)))==types.GeneratorType
True
  1. isinstance()可以一個物件是否是某種型別。isinstance()判斷的是一個物件是否是該型別本身,或者位於該型別的父繼承鏈上。

  2. 父類的例項不是繼承它的子類的型別。

  3. 能用type()判斷的基本型別也可以用isinstance()判斷,並且還可以判斷一個變數是否是某些型別中的一種,比如下面的程式碼就可以判斷是否是list或者tuple:

>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True
  1. dir():
    獲得一個物件的所有屬性和方法,它返回一個包含字串的list。

  2. 類似xxx的屬性和方法在Python中都是有特殊用途的,比如len方法返回長度。

  3. getattr():getattr(obj, 'y') # 獲取屬性'y'

  4. setattr():setattr(obj, 'y', 19) # 設定一個屬性'y'

  5. hasattr():hasattr(obj, 'x') # 有屬性'x'嗎?

  6. 如果試圖獲取不存在的屬性,會丟擲AttributeError的錯誤。

  7. 只有在不知道物件資訊的時候,我們才會去獲取物件資訊。
    假設我們希望從檔案流fp中讀取影象,我們首先要判斷該fp物件是否存在read方法,如果存在,則該物件是一個流,如果不存在,則無法讀取。hasattr()就派上了用場。

def readImage(fp):
    if hasattr(fp, 'read'):
        return readData(fp)
    return None

例項屬性和類屬性

由於Python是動態語言,根據類建立的例項可以任意繫結屬性。

在編寫程式的時候,千萬不要把例項屬性和類屬性使用相同的名字,因為相同名稱的例項屬性將遮蔽掉類屬性,但是當你刪除例項屬性後,再使用相同的名稱,訪問到的將是類屬性。

學習網站:www.liaoxuefeng.com