理解面向對象,首先理解要它的基礎概念:
面向對象 ( Object Oriented ) 是將現實問題構建關系,然后抽象成 類 ( class ),給類定義屬性和方法后,再將類實例化成 實例 ( instance ) ,通過訪問實例的屬性和調用方法來進行使用。
在不同的語言中,對象的定義范圍不同。在 Python 中“類”和“類的實例”都稱為 對象 ( Object ),因為 Python 的類是更頂級的 type 實例化后的對象,也就是常說的“Python 里萬物皆對象”;而在 Java 等靜態語言中,一般把類的實例稱為對象。
理解了理論知識后,接着通過例子,再理解面向對象的三大特征:封裝、繼承、多態。
下邊我們把“女媧造人”這個神話故事,用 Python 的面向對象代碼來敘述一遍:
假設我們是女媧(程序設計者),我們突然有個想法,想造一群和自己差不多的小人,小人需要有男女兩種性別,外觀和行為也有一些差異。那首先我們分析出,不管什么性別,都應該有四肢,所以我們先仿照自己的構造,在腦海中構思泥人的樣子(抽象成基類),然后先賦予泥人一些共有的行為(定義類的實例方法):
class Human(object): def __init__(self, name): # 有個名字,有兩只手,兩條腿 self._name = name self.hands = 2 self.legs = 2 def introduce_self(self): # 介紹自己 print('我是%s' % self._name) def work(self): # 工作,但還沒有定義具體的行為 raise NotImplementedError
然后我們先捏3個泥人(實例化對象),並給他們取了不同的名字(初始化實例屬性):
>>> a = Human('大強子') >>> b = Human('二狗子') >>> c = Human('三愣子')
我們讓其中一個人介紹自己(調用實例方法):
>>> a.introduce_self() 我是大強子
這里解釋一下 Human
的代碼,雖然設定了每個人都要工作,但如何工作需要到具體到不同類型的人,所以在基類里我們並沒有定義 work
方法的內容,如果強行調用會拋出異常。
還有一點,上面定義屬性時,我們把 self._name
前邊加了下划線,是因為 Python 里用下划線來約定這是一個受保護變量(對應 Java 中的 protected
),我們不希望外界能直接訪問 name
這個屬性,必須要通過對象調用 introduce_self()
這個行為介紹了自己,別人才能知道他叫什么名字,這個過程就稱之為封裝。
然后我們繼續完成想法,需要給泥人增加兩種性別,並且異性之間能結婚,我們開始在剛才泥人模型的基礎上(繼承於基類),構思出兩種性別的泥人的區別(設置不同的屬性),然后讓他們都可以工作,但工作的內容不一樣(調用相同的方法出現不同結果,是多態性),並決定讓男人可以娶女人(將這個行為定義為男人的方法)。
import random class Female(Human): def __init__(self, name): # 調用父類的初始化方法,依然有名字、兩只手、兩條腿 super().__init__(name) # 頭發和力量進行隨機取值 self.hair = random.randint(3, 5) self.power = random.randint(1, 3) # 是否已婚 self.married = False def work(): print('%s采摘了一些果子' % self.name) class Male(Human): def __init__(self, name): super().__init__(name) self.hair = random.randint(0, 2) self.power = random.randint(2, 5) self.married = False def work(): print('%s出去打獵了一天' % self.name) def marry(self, other): # 判斷自己或對方是否已結婚,否則拋出異常 if self.married is True or other.married is True: raise ValueError('法律不支持多次結婚') # 判斷對方是否是女性,否則拋出異常 if isinstance(other, Female): self.married = True other.married = True else: raise TypeError('法律不支持同性結婚')
然后我們就可以讓小人活動起來:
>>> a = Male('大強子') >>> b = Male('二狗子') >>> c = Female('翠花') >>> for h in [a, b, c]: ... # 調用父類的方法 ... h.introduce_self() 我是大強子 我是二狗子 我是翠花 >>> for h in [a, b, c]: ... # 多態性使相同的方法產生不同的結果 ... h.work() 大強子出去打獵了一天 二狗子出去打獵了一天 翠花采摘了一些果子 >>> a.marry(c) >>> a.married True >>> c.married True >>> b.marry(c) ValueError: 法律不支持多次結婚 >>> b.marry(a) TypeError: 法律不支持同性結婚
設計到此結束,我們來復盤一下整個過程。
我們先是把人的共有特征抽象成 Human
基類,這個基類並不用於實例化,而是用於讓 Female
和 Male
繼承它,並實現不同的行為。這樣我們就避免把一些共有的行為重復在多個類里定義,如果我們后續想對人類的行為進行變動,也只需要修改 Human
,繼承 Human
的子類會自動獲得新行為,這是 繼承帶來的好處。
我們把 name
設計為受保護變量,外界無法直接訪問這個屬性,讓每個人的隱私得到了保障(一些不必要的行為變得可控),這是 封裝 帶來的好處。
同時我們在 Human
中預留了 work
方法,並在 Female
和 Male
都實現了不同的效果,然后我們知道人人都有 work
方法,因此可以像 introduce_self
一樣,用循環批量調用 work
方法,這是 多態 帶來的好處。
看到這里你應該有些理解:面向對象是將客觀事物和一些關系,抽象成具體的模型(類),並為其設計屬性和方法,即 對象 = 屬性(特征)+ 方法(行為)。
如果是擁有復雜關系的需求,我們就應該盡可能將互相有關聯的行為抽象成類,比如每一個網頁,網頁中每一個組件 等等。實際上面向對象幫助我們在幾萬行代碼的大型項目中,仍然可以游刃有余,正因為如此,才能發展為目前應用最為廣泛的編程思想。
但也並不是說任何時候都要“面向對象”,過度的封裝和抽象,也會造成代碼可讀性的下降,以及運行效率的下降,因此我們應該在能將事物抽象化的需求中使用面向對象。
最后,不管是面向什么編程,終究還是要面向人生
歡迎關注我的微信公眾號:面向人生編程
回復【資料】獲取本人精選的學習視頻及代碼