python-面向?qū)ο缶幊?/h1>

面向?qū)ο缶幊袒A

活在當下的程序員應該都聽過“面向?qū)ο缶幊獭币辉~,也經(jīng)常有人問能不能用一句話解釋下什么是“面向?qū)ο缶幊獭蓖蕹校覀兿葋砜纯幢容^正式的說法奏夫。

把一組數(shù)據(jù)結(jié)構(gòu)和處理它們的方法組成對象(object),把相同行為的對象歸納為類(class)历筝,通過類的封裝(encapsulation)隱藏內(nèi)部細節(jié)酗昼,通過繼承(inheritance)實現(xiàn)類的特化(specialization)和泛化(generalization),通過多態(tài)(polymorphism)實現(xiàn)基于對象類型的動態(tài)分派梳猪。

這樣一說是不是更不明白了麻削。所以我們還是看看更通俗易懂的說法,下面這段內(nèi)容來自于知乎舔示。

[圖片上傳失敗...(image-8caba1-1563173606674)]

說明: 以上的內(nèi)容來自于網(wǎng)絡碟婆,不代表作者本人的觀點和看法,與作者本人立場無關(guān)惕稻,相關(guān)責任不由作者承擔竖共。

之前我們說過“程序是指令的集合”,我們在程序中書寫的語句在執(zhí)行時會變成一條或多條指令然后由CPU去執(zhí)行俺祠。當然為了簡化程序的設計公给,我們引入了函數(shù)的概念,把相對獨立且經(jīng)常重復使用的代碼放置到函數(shù)中蜘渣,在需要使用這些功能的時候只要調(diào)用函數(shù)即可淌铐;如果一個函數(shù)的功能過于復雜和臃腫,我們又可以進一步將函數(shù)繼續(xù)切分為子函數(shù)來降低系統(tǒng)的復雜性蔫缸。但是說了這么多腿准,不知道大家是否發(fā)現(xiàn),所謂編程就是程序員按照計算機的工作方式控制計算機完成各種任務拾碌。但是吐葱,計算機的工作方式與正常人類的思維模式是不同的,如果編程就必須得拋棄人類正常的思維方式去迎合計算機校翔,編程的樂趣就少了很多弟跑,“每個人都應該學習編程”這樣的豪言壯語就只能說說而已。當然防症,這些還不是最重要的孟辑,最重要的是當我們需要開發(fā)一個復雜的系統(tǒng)時,代碼的復雜性會讓開發(fā)和維護工作都變得舉步維艱蔫敲,所以在上世紀60年代末期饲嗽,“軟件危機”、“軟件工程”等一系列的概念開始在行業(yè)中出現(xiàn)燕偶。

當然喝噪,程序員圈子內(nèi)的人都知道,現(xiàn)實中并沒有解決上面所說的這些問題的“銀彈”指么,真正讓軟件開發(fā)者看到希望的是上世紀70年代誕生的Smalltalk編程語言中引入的面向?qū)ο蟮木幊趟枷耄嫦驅(qū)ο缶幊痰碾r形可以追溯到更早期的Simula語言)酝惧。按照這種編程理念,程序中的數(shù)據(jù)和操作數(shù)據(jù)的函數(shù)是一個邏輯上的整體伯诬,我們稱之為“對象”晚唇,而我們解決問題的方式就是創(chuàng)建出需要的對象并向?qū)ο蟀l(fā)出各種各樣的消息,多個對象的協(xié)同工作最終可以讓我們構(gòu)造出復雜的系統(tǒng)來解決現(xiàn)實中的問題盗似。

說明: 當然面向?qū)ο笠膊皇墙鉀Q軟件開發(fā)中所有問題的最后的“銀彈”哩陕,所以今天的高級程序設計語言幾乎都提供了對多種編程范式的支持,Python也不例外赫舒。

類和對象

簡單的說悍及,類是對象的藍圖和模板,而對象是類的實例接癌。這個解釋雖然有點像用概念在解釋概念心赶,但是從這句話我們至少可以看出,類是抽象的概念缺猛,而對象是具體的東西缨叫。在面向?qū)ο缶幊痰氖澜缰校磺薪詾閷ο罄罅牵瑢ο蠖加袑傩院托袨槌芾眩總€對象都是獨一無二的,而且對象一定屬于某個類(型)有咨。當我們把一大堆擁有共同特征的對象的靜態(tài)特征(屬性)和動態(tài)特征(行為)都抽取出來后琐簇,就可以定義出一個叫做“類”的東西。

[圖片上傳失敗...(image-21da51-1563173606674)]

定義類

在Python中可以使用class關(guān)鍵字定義類座享,然后在類中通過之前學習過的函數(shù)來定義方法婉商,這樣就可以將對象的動態(tài)特征描述出來,代碼如下所示征讲。

class Student(object):

    # __init__是一個特殊方法用于在創(chuàng)建對象時進行初始化操作
    # 通過這個方法我們可以為學生對象綁定name和age兩個屬性
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def study(self, course_name):
        print('%s正在學習%s.' % (self.name, course_name))

    # PEP 8要求標識符的名字用全小寫多個單詞用下劃線連接
    # 但是部分程序員和公司更傾向于使用駝峰命名法(駝峰標識)
    def watch_movie(self):
        if self.age < 18:
            print('%s只能觀看《熊出沒》.' % self.name)
        else:
            print('%s正在觀看島國愛情大電影.' % self.name)

說明: 寫在類中的函數(shù)据某,我們通常稱之為(對象的)方法,這些方法就是對象可以接收的消息诗箍。

創(chuàng)建和使用對象

當我們定義好一個類之后癣籽,可以通過下面的方式來創(chuàng)建對象并給對象發(fā)消息。

def main():
    # 創(chuàng)建學生對象并指定姓名和年齡
    stu1 = Student('駱昊', 38)
    # 給對象發(fā)study消息
    stu1.study('Python程序設計')
    # 給對象發(fā)watch_av消息
    stu1.watch_movie()
    stu2 = Student('王大錘', 15)
    stu2.study('思想品德')
    stu2.watch_movie()


if __name__ == '__main__':
    main()

訪問可見性問題

對于上面的代碼滤祖,有C++筷狼、Java、C#等編程經(jīng)驗的程序員可能會問匠童,我們給Student對象綁定的nameage屬性到底具有怎樣的訪問權(quán)限(也稱為可見性)埂材。因為在很多面向?qū)ο缶幊陶Z言中,我們通常會將對象的屬性設置為私有的(private)或受保護的(protected)汤求,簡單的說就是不允許外界訪問俏险,而對象的方法通常都是公開的(public)严拒,因為公開的方法就是對象能夠接受的消息。在Python中竖独,屬性和方法的訪問權(quán)限只有兩種裤唠,也就是公開的和私有的,如果希望屬性是私有的莹痢,在給屬性命名時可以用兩個下劃線作為開頭种蘸,下面的代碼可以驗證這一點。

class Test:

    def __init__(self, foo):
        self.__foo = foo

    def __bar(self):
        print(self.__foo)
        print('__bar')


def main():
    test = Test('hello')
    # AttributeError: 'Test' object has no attribute '__bar'
    test.__bar()
    # AttributeError: 'Test' object has no attribute '__foo'
    print(test.__foo)


if __name__ == "__main__":
    main()

但是竞膳,Python并沒有從語法上嚴格保證私有屬性或方法的私密性航瞭,它只是給私有的屬性和方法換了一個名字來“妨礙”對它們的訪問,事實上如果你知道更換名字的規(guī)則仍然可以訪問到它們坦辟,下面的代碼就可以驗證這一點刊侯。之所以這樣設定,可以用這樣一句名言加以解釋长窄,就是“We are all consenting adults here”滔吠。因為絕大多數(shù)程序員都認為開放比封閉要好,而且程序員要自己為自己的行為負責挠日。

class Test:

    def __init__(self, foo):
        self.__foo = foo

    def __bar(self):
        print(self.__foo)
        print('__bar')


def main():
    test = Test('hello')
    test._Test__bar()
    print(test._Test__foo)


if __name__ == "__main__":
    main()

在實際開發(fā)中疮绷,我們并不建議將屬性設置為私有的,因為這會導致子類無法訪問(后面會講到)嚣潜。所以大多數(shù)Python程序員會遵循一種命名慣例就是讓屬性名以單下劃線開頭來表示屬性是受保護的冬骚,本類之外的代碼在訪問這樣的屬性時應該要保持慎重。這種做法并不是語法上的規(guī)則懂算,單下劃線開頭的屬性和方法外界仍然是可以訪問的只冻,所以更多的時候它是一種暗示或隱喻,關(guān)于這一點可以看看我的《Python - 那些年我們踩過的那些坑》文章中的講解计技。

面向?qū)ο蟮闹е?/h3>

面向?qū)ο笥腥笾е悍庋b喜德、繼承和多態(tài)。后面兩個概念在下一個章節(jié)中進行詳細的說明垮媒,這里我們先說一下什么是封裝舍悯。我自己對封裝的理解是“隱藏一切可以隱藏的實現(xiàn)細節(jié),只向外界暴露(提供)簡單的編程接口”睡雇。我們在類中定義的方法其實就是把數(shù)據(jù)和對數(shù)據(jù)的操作封裝起來了萌衬,在我們創(chuàng)建了對象之后,只需要給對象發(fā)送一個消息(調(diào)用方法)就可以執(zhí)行方法中的代碼它抱,也就是說我們只需要知道方法的名字和傳入的參數(shù)(方法的外部視圖)秕豫,而不需要知道方法內(nèi)部的實現(xiàn)細節(jié)(方法的內(nèi)部視圖)。

練習

練習1:定義一個類描述數(shù)字時鐘

from time import sleep


class Clock(object):
    """數(shù)字時鐘"""

    def __init__(self, hour=0, minute=0, second=0):
        """初始化方法

        :param hour: 時
        :param minute: 分
        :param second: 秒
        """
        self._hour = hour
        self._minute = minute
        self._second = second

    def run(self):
        """走字"""
        self._second += 1
        if self._second == 60:
            self._second = 0
            self._minute += 1
            if self._minute == 60:
                self._minute = 0
                self._hour += 1
                if self._hour == 24:
                    self._hour = 0

    def show(self):
        """顯示時間"""
        return '%02d:%02d:%02d' % \
               (self._hour, self._minute, self._second)


def main():
    clock = Clock(23, 59, 58)
    while True:
        print(clock.show())
        sleep(1)
        clock.run()


if __name__ == '__main__':
    main()

練習2:定義一個類描述平面上的點并提供移動點和計算到另一個點距離的方法观蓄。

from math import sqrt


class Point(object):

    def __init__(self, x=0, y=0):
        """初始化方法
        
        :param x: 橫坐標
        :param y: 縱坐標
        """
        self.x = x
        self.y = y

    def move_to(self, x, y):
        """移動到指定位置
        
        :param x: 新的橫坐標
        "param y: 新的縱坐標
        """
        self.x = x
        self.y = y

    def move_by(self, dx, dy):
        """移動指定的增量
        
        :param dx: 橫坐標的增量
        "param dy: 縱坐標的增量
        """
        self.x += dx
        self.y += dy

    def distance_to(self, other):
        """計算與另一個點的距離
        
        :param other: 另一個點
        """
        dx = self.x - other.x
        dy = self.y - other.y
        return sqrt(dx ** 2 + dy ** 2)

    def __str__(self):
        return '(%s, %s)' % (str(self.x), str(self.y))


def main():
    p1 = Point(3, 5)
    p2 = Point()
    print(p1)
    print(p2)
    p2.move_by(-1, 2)
    print(p2)
    print(p1.distance_to(p2))


if __name__ == '__main__':
    main()

說明: 本章中的插圖來自于Grady Booch等著作的《面向?qū)ο蠓治雠c設計》一書混移,該書是講解面向?qū)ο缶幊痰慕?jīng)典著作祠墅,有興趣的讀者可以購買和閱讀這本書來了解更多的面向?qū)ο蟮南嚓P(guān)知識。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者

  • 序言:七十年代末沫屡,一起剝皮案震驚了整個濱河市饵隙,隨后出現(xiàn)的幾起案子撮珠,更是在濱河造成了極大的恐慌沮脖,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芯急,死亡現(xiàn)場離奇詭異勺届,居然都是意外死亡,警方通過查閱死者的電腦和手機娶耍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門免姿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人榕酒,你說我怎么就攤上這事胚膊。” “怎么了想鹰?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵紊婉,是天一觀的道長。 經(jīng)常有香客問我辑舷,道長喻犁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任何缓,我火速辦了婚禮肢础,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘碌廓。我一直安慰自己传轰,他們只是感情好,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布谷婆。 她就那樣靜靜地躺著慨蛙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪波材。 梳的紋絲不亂的頭發(fā)上股淡,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機與錄音廷区,去河邊找鬼唯灵。 笑死,一個胖子當著我的面吹牛隙轻,可吹牛的內(nèi)容都是我干的埠帕。 我是一名探鬼主播垢揩,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼敛瓷!你這毒婦竟也來了叁巨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤呐籽,失蹤者是張志新(化名)和其女友劉穎锋勺,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狡蝶,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡庶橱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了贪惹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苏章。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖奏瞬,靈堂內(nèi)的尸體忽然破棺而出枫绅,到底是詐尸還是另有隱情,我是刑警寧澤硼端,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布并淋,位于F島的核電站,受9級特大地震影響显蝌,放射性物質(zhì)發(fā)生泄漏预伺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一曼尊、第九天 我趴在偏房一處隱蔽的房頂上張望酬诀。 院中可真熱鬧,春花似錦骆撇、人聲如沸瞒御。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肴裙。三九已至,卻和暖如春涌乳,著一層夾襖步出監(jiān)牢的瞬間蜻懦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工夕晓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宛乃,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像征炼,于是被迫代替她去往敵國和親析既。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容