廖雪峯Python教程閱讀筆記——6. 面向對象編程

6.面向對象編程

OOP是一種設計思想,OOP把對象作爲程序的基元。一個對象包括屬性和操作屬性的方法。
在Python中,所有的對象都可視爲對象,當然也可以自定義對象。自定義的對象數據類型,就是面向對象中的類(class)。
設想打印學生的成績,學生有姓名和成績兩種屬性。我們創建一個學生對象Student,有姓名name和成績score兩個屬性。如果要打印學生的成績,必須創建出一個學生對象出來,然後使用print_score,讓對象把自己的數據打印出來。

'測試學生對象'
class Student(self, name, score):
    '學生類'
    def __init__(self):
        '初始化對象,類似於構造函數'
        self.name = name
        self.score = score

    def print_score(self):
        '打印輸出'
        print("%s, %s" % (self.name, self.score))

6.1類和實例

面向對象的最重要的概念就是類(class)和實例(instance)。在Python中用關鍵字class緊跟類名,來定義類。定義好了類,可以使用類名+()來實例化:

>>>bart = Student('Minie',69)

該語句,是創建一個student對象,並將變量bart指向該對象。
注意到,上面定義的類,__init__方法第一個參數是self,表示創建實例本身。有了__init__方法,實例化對象時,就不能不傳參數了,必須傳入與__init__方法匹配的參數。類似於java的構造函數。
和普通函數相比,在類中定義的函數,第一個參數永遠是實例變量self,並且調用時,不需要傳該參數。除此之外,類的方法和普通函數沒有區別。
- 數據封裝
如上述的例子,student對象,在類中,定義了一些數據姓名name和分數score。這些數據的函數是和student類本身關聯起來的。

小結:類是創建實例的模板,而實例是一個一個的具體的對象,各個實例擁有的數據都互相獨立。方法就是與實例綁定的函數,與普通函數不同,方法可以直接操作類中的數據。

6.2訪問限制

在class內部,可以有屬性和方法,而外部代碼可以直接調用實例來操作數據。但是從前面定義的Student類來看,外部代碼還是可以自由的修改實例的name、score屬性:

>>>bart = Student('Bartun',90)
>>>bart.score
90
>>>bart.score=80
>>>bart.score
80

如果要讓內部屬性不被外部改變,則把屬性前面加兩個_,在Python中,實例的變量如果用__開頭,就變成了一個私有變量,只有內部可以訪問,外部不能訪問。
我們把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。如果想要訪問,就必須加getset方法

注意:在Python中,變量名類似__XXX__開頭和結尾都是__的,說明是特殊變量。特殊變量時可以直接訪問的,不是Private的。有時候類似_xxx這種變量,這種變量雖然可以被直接訪問,但是按照約定的規則,視此類型的變量爲私有變量,不要隨意訪問。

6.3繼承和多態

在OOP程序中,我們定義了一個類(class),該類可以被繼承。
定義一個Animal類,有run()方法和eat()方法,再定義一個cat和dog類,也有run方法。當子類和父類都有相同的方法時,子類方法覆蓋掉父類方法,這種現象稱爲多態。
在繼承關係中,子類的數據類型是屬於父類的數據類型。

動態語言 VS 靜態語言
對於類的繼承,如果是靜態語言(Java),繼承時傳入的類型必須是父類型或者其子類。否則無法調用run()方法。
對於Python類型,不一定是父類型,只要保證傳入的類型中有run()方法就行。這就是Python的”file-like object”鴨子類型。

小結:
繼承可以把父類的所有功能繼承下來,也可以重載。動態語言的鴨子類型特點決定了繼承不像靜態語言那樣是必須的。

6.4獲取對象信息

當我們拿到一個對象的引用時,如何知道這個對象是什麼類型,有哪些方法?

  • 獲取類型type()
    通過type()可以獲取對象的類型:
>>> type(123)
<class 'int'>
>>> type('ab')
<class 'str'>
>>> import math                                      
>>> type(abs)     ##獲取函數的類型
<class 'builtin_function_or_method'>

可以通過是否相等,判斷兩個對象的類型是否一致,如果對於繼承關係的對象,最好用isinstance()函數。
- 獲取方法dir()
如果要獲取一個對象的所有屬性和方法,可以使用dir()函數。它返回一個包含所有字符串的list。

>>> dir('abc')                                                                                                                              
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getit
em__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul
__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subc
lasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isal
num', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'lj
ust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitl
ines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']                                                           

其中,不乏有__XXX__的屬性和方法,這在Python中是有特殊用途的,比如__len__方法返回長度。在Python中,你調用了len()函數,會返回一個對象的長度。實際上,在lne()內部,它會自動調用該對象的__len__方法。
下面代碼是等價的:

>>> len('abc')                                        
3                                                   
>>> 'abc'.__len__()                                                   
3 

剩下的都是普通屬性和方法。
再配合getattr()setattr()hasattr()可以直接操作一個對象的狀態。

小結:
通過一系列內置函數,我們可以對任意一個對象進行剖析,但是隻有在不知道對象信息的時候,我們纔會獲取對象信息。

6.5實例屬性和類屬性

略.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章