【Python】面向對象編程

2018-6-6

面向對象編程——Object Oriented Programming,簡稱OOP。
程序的基本單元:對象
對象:包含了數據和操作數據的函數
在Python中,萬物皆爲對象。(程序員後宮佳麗三千喲)
所有的數據類型都可視爲對象,
對象也可以是自己創造的。
自己創造—自定義的對象數據類型就是類(class)。

Class是一種抽象概念,比如我們定義的Class——Student,是指學生這個概念,而實例(Instance)則是一個個具體的Student。
面向對象的設計思想是抽象出Class,根據Class創建Instance。


類和實例

class Student(object):
    pass

class後面跟的是類名——Student,注意,類名通常是大寫開頭的單詞。
後面的(object),表示這個類是從哪個類繼承下來的。如果沒有合適的繼承類,就使用object類,這是所有類最終都會繼承的類。【想想樹枝】
類定義以後就可以創建其實例:

bart = Student()

變量bart指向的就是一個Student的實例。

可以給實例變量bart綁定一個屬性name

bart.name = 'Bart Simpson'

啊,類就像女媧捏泥人兒的模子,女媧捏人兒(創建實例)的時候,就得把一些個她認爲必須綁定的屬性強制填寫進去。那她就用了__init__方法:

class Student(object):

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

應當注意,
注意到__init__方法的第一個參數永遠是self,表示創建的實例本身,因此,在__init__方法內部,就可以把各種屬性綁定到self,因爲self就指向創建的實例本身。


數據封裝
一個實例本身是擁有一些屬性的數據的,可以通過函數來訪問。
但是可以通過Student類的內部定義訪問數據的函數,這樣子,就相當於把數據給裝到黑匣子了嘛~並且這些封裝數據的函數是和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))

數據封裝的第二個好處是:
可以給Student類增加新的方法,比如get_grade

class Student(object):
    ...

    def get_grade(self):
        if self.score >= 90:
            return 'A'
        elif self.score >= 60:
            return 'B'
        else:
            return 'C'

2018-6-7
訪問限制
在Python中,實例的變量名如果以__開頭,就變成了一個私有變量(private),只有內部可以訪問,外部不能訪問。

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

啊,這樣的話連訪問都不可以了,如果需要訪問的話需要給Student類增加get_nameget_score這樣的方法。

class Student(object):
    ...

    def get_name(self):
        return self.__name

    def get_score(self):
        return self.__score

這時候腦子抽了又想允許外部代碼修改score了(事兒多得很),這時候可以增加set_score方法。

class Student(object):
    ...

    def set_score(self, score):
        self.__score = score

繼承和多態
在OOP中,我們可以通過一個現有的class繼承來定義一個class,這個新的class就稱爲子類——subclass,現有的被繼承的class稱爲基類、父類或者超類——Base class、Super class。

class Animal(object):
    def run(self):
        print('Animal is running...')

class Dog(Animal):
    pass

class Cat(Animal):
    pass

上面的例子中,Animal是父類,DogCat就是子類。
這樣做的目的是,子類擁有了父類全部的功能——在本例中實現了run()方法。


2018-6-9
北京,雨
多態的好處:
對於一個變量,我們只需要知道它是什麼父類型,而無需知道它的子類型,就可以使用其方法——調用方只需要調用,而不需要知道細節。在增加一種子類時,只需要保證方法正確,不用管原來的代碼是如何調用的。也就是“開閉原則”:

對擴展開放:允許新增Animal子類;
對修改封閉:不需要修改依賴Animal類型的run_twice()等函數。

從具體理解上看,繼承就像是樹一樣:

這裏寫圖片描述

靜態語言 vs 動態語言

對於靜態語言(例如Java)來說,如果需要傳入Animal類型,則傳入的對象必須是Animal類型或者它的子類,否則,將無法調用run()方法。
對於Python這樣的動態語言來說,則不一定需要傳入Animal類型。我們只需要保證傳入的對象有一個run()方法就可以了:

class Timer(object):
    def run(self):
        print('Start...')

獲取對象信息
type()
判斷對象是哪種基本數據類型,還是函數

>>> type(123)==int
True

>>> 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

isinstance()
原文稱:對於class的繼承關係來說,使用type()不方便。
爲什麼呢?
Python type() 函數中談及isinstance()type()區別:

  • type() 不會認爲子類是一種父類類型,不考慮繼承關係。
  • isinstance() 會認爲子類是一種父類類型,考慮繼承關係。
    如果要判斷兩個類型是否相同推薦使用 isinstance()。

dir()
可以獲取一個對象的所有屬性和方法。

>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']

配合getattr()setattr()以及hasattr(),可以直接操作一個對象的狀態。
getattr():用於返回一個對象的屬性值——getattr(object, name[, default])
setattr():用於設置屬性值,該屬性必須存在——setattr(object, name, value)
hasattr():用於判斷對象是否包含對應的屬性——hasattr(object, name)


實例屬性和類屬性

實例屬性屬於各個實例所有,互不干擾;
類屬性屬於類所有,所有實例共享一個屬性;
不要對實例屬性和類屬性使用相同的名字,否則將產生難以發現的錯誤。

爲了統計學生人數,可以給Student類增加一個類屬性,每創建一個實例,該屬性自動增加:

class Student(object):
    count = 0
    def __init__(self, name):
        self.name = name
        Student.count += 1
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章