App下載

面向?qū)ο缶幊碳捌淙筇匦?/h1>
猿友 2021-02-02 13:41:41 瀏覽數(shù) (3338)
反饋

編程語言分為面向過程編程、函數(shù)式編程和面向?qū)ο缶幊?。其?shí) python 就是一種面向?qū)ο缶幊蹋敲次覀兿攘私庖幌滤鼈兊奶攸c(diǎn)和優(yōu)缺點(diǎn)以及它們的區(qū)別是什么。

面向過程編程:“面向過程”(Procedure Oriented)是一種以過程為中心的編程思想。這些都是以什么正在發(fā)生為 目標(biāo)進(jìn)行編程,不同于面向?qū)ο蟮氖钦l在受影響。與面向?qū)ο竺黠@的不同就是封裝、繼承、類。

面向過程編程最易被初學(xué)者接受,其往往用一長段代碼來實(shí)現(xiàn)指定功能,開發(fā)過程的思路是將數(shù)據(jù)與函數(shù)按照執(zhí)行的邏輯順序組織在一起,數(shù)據(jù)與函數(shù)分開考慮。

  • 特性:模塊化 流程化
  • 優(yōu)點(diǎn):性能比面向?qū)ο蟾?,因?yàn)轭愓{(diào)用時(shí)需要實(shí)例化,開銷比較大,比較消耗資源;單片機(jī)、嵌入式開發(fā)、Linux/Unix 等一般采用面向過程開發(fā),性能是最重要的因素。
  • 缺點(diǎn):沒有面向?qū)ο笠拙S護(hù)、易復(fù)用、易擴(kuò)展

函數(shù)式編程: 函數(shù)式編程也是種編程方式,它將電腦運(yùn)算視為函數(shù)的計(jì)算。函數(shù)編程語言最重要的基礎(chǔ)是λ演算(lambda calculus),而且 λ 演算的函數(shù)可以接受函數(shù)當(dāng)作輸入(參數(shù))和輸出(返回值)。

它的主要思想是把運(yùn)算過程盡量寫成一系列嵌套的函數(shù)調(diào)用。

Python 不是也不大可能會成為一種函數(shù)式編程語言,但是它支持許多有價(jià)值的函數(shù)式編程語言構(gòu)建。也有些表現(xiàn)得像函數(shù)式編程機(jī)制但是從傳統(tǒng)上也不能被認(rèn)為是函數(shù)式編程語言的構(gòu)建。

Python內(nèi)建函數(shù) : filter()、map()、reduce()、max()、min()

面向?qū)ο缶幊蹋好嫦驅(qū)ο笫前慈藗冋J(rèn)識客觀世界的系統(tǒng)思維方式,采用基于對象(實(shí)體)的概念建立模型,模擬客觀世界分析、設(shè)計(jì)、實(shí)現(xiàn)軟件的辦法。通過面向?qū)ο蟮睦砟钍褂?jì)算機(jī)軟件系統(tǒng)能與現(xiàn)實(shí)世界中的系統(tǒng)一一對應(yīng)。

面向?qū)ο缶幊炭梢詫?shù)據(jù)與函數(shù)綁定到一起,進(jìn)行封裝,這樣能夠更快速的開發(fā)程序,減少了重復(fù)代碼的重寫過程。

  • 特性:抽象 封裝 繼承 多態(tài)
  • 優(yōu)點(diǎn):易維護(hù)、易復(fù)用、易擴(kuò)展,由于面向?qū)ο笥蟹庋b、繼承、多態(tài)性的特性,可以設(shè)計(jì)出低耦合 的系統(tǒng),使系統(tǒng)更加靈活、更加易于維護(hù)
  • 缺點(diǎn):性能比面向過程低

可以拿生活中的實(shí)例來理解面向過程與面向?qū)ο?,例如五子棋,面向過程的設(shè)計(jì)思路就是首先分析問題的步驟:1、開始游戲,2、黑子先走,3、繪制畫面,4、判斷輸贏,5、輪到白子,6、繪制畫面,7、判斷輸贏,8、返回步驟2,9、輸出最后結(jié)果。把上面每個(gè)步驟用不同的方法來實(shí)現(xiàn)。

如果是面向?qū)ο蟮脑O(shè)計(jì)思想來解決問題。面向?qū)ο蟮脑O(shè)計(jì)則是從另外的思路來解決問題。整個(gè)五子棋可以分為1、黑白雙方,這兩方的行為是一模一樣的,2、棋盤系統(tǒng),負(fù)責(zé)繪制畫面,3、規(guī)則系統(tǒng),負(fù)責(zé)判定諸如犯規(guī)、輸贏等。第一類對象(玩家對象)負(fù)責(zé)接受用戶輸入,并告知第二類對象(棋盤對象)棋子布局的變化,棋盤對象接收到了棋子的變化就要負(fù)責(zé)在屏幕上面顯示出這種變化,同時(shí)利用第三類對象(規(guī)則系統(tǒng))來對棋局進(jìn)行判定。

可以明顯地看出,面向?qū)ο笫且怨δ軄韯澐謫栴},而不是步驟。同樣是繪制棋局,這樣的行為在面向過程的設(shè)計(jì)中分散在了多個(gè)步驟中,很可能出現(xiàn)不同的繪制版本,因?yàn)橥ǔTO(shè)計(jì)人員會考慮到實(shí)際情況進(jìn)行各種各樣的簡化。而面向?qū)ο蟮脑O(shè)計(jì)中,繪圖只可能在棋盤對象中出現(xiàn),從而保證了繪圖的統(tǒng)一。

面向?qū)ο缶幊倘筇匦?/h1>

在看面向?qū)ο缶幊倘筇匦灾?,先了解一下對象和類的區(qū)別。

對象和類

類(Class)是現(xiàn)實(shí)或思維世界中的實(shí)體在計(jì)算機(jī)中的反映,它將數(shù)據(jù)以及這些數(shù)據(jù)上的操作封裝在一起。類實(shí)際上是創(chuàng)建實(shí)例的模板。

對象(Object)是具有類類型的變量。類和對象是面向?qū)ο缶幊碳夹g(shù)中的最基本的概念。 而對象就是一個(gè)一個(gè)具體的實(shí)例。

定義類的方法:

class 類(): pass

那么如何將類轉(zhuǎn)換成對象呢?

實(shí)例化是指在面向?qū)ο蟮木幊讨校延妙悇?chuàng)建對象的過程稱為實(shí)例化。是將一個(gè)抽象的概念類,具體到該類實(shí)物的過程。實(shí)例化過程中一般由類名 對象名 = 類名(參數(shù)1,參數(shù)2...參數(shù)n)構(gòu)成。

定義類之后一般會用到構(gòu)造方法 init 與其他普通方法不同的地方在于,當(dāng)一個(gè)對象被創(chuàng)建后,會立即調(diào)用構(gòu)造方法(也稱為魔術(shù)方法)。自動執(zhí)行構(gòu)造方法里面的內(nèi)容。

1.封裝特性

封裝,顧名思義就是將內(nèi)容封裝到某個(gè)地方,以后再去調(diào)用被封裝在某處的內(nèi)容。 所以,在使用面向?qū)ο蟮姆庋b特性時(shí),需要:

  1. 將內(nèi)容封裝到某處
  2. 從某處調(diào)用被封裝的內(nèi)容,調(diào)用方法如下:
  • 通過對象直接調(diào)用被封裝的內(nèi)容: 對象.屬性名
  • 通過 self 間接調(diào)用被封裝的內(nèi)容: self.屬性名
  • 通過 self 間接調(diào)用被封裝的內(nèi)容: self.方法名()

對于面向?qū)ο蟮姆庋b來說,其實(shí)就是使用構(gòu)造方法將內(nèi)容封裝到 對象 中,然后通過 對象直接或者 self 間接獲取被封裝的內(nèi)容。

具體例子如下:

# 1). 類的定義
class People:
   # 構(gòu)造方法(魔術(shù)方法): 當(dāng)創(chuàng)建對象時(shí)會自動調(diào)用并執(zhí)行;
   # self實(shí)質(zhì)上是實(shí)例化出來的對象, e.g: xiaoming, xiaohong;
   def __init__(self, name, age, gender):
      # print("正在創(chuàng)建對象")
       # print(self) # 實(shí)質(zhì)上是一個(gè)對象
       # 將創(chuàng)建對象的屬性(name, age, gender)封裝到self(就是實(shí)例化的對象)變量里面;
       # 在類里面定義的變量: 屬性
       self.name = name
       self.age = age
       self.gender = gender
   # 在類里面定義的函數(shù): 方法
   def eat(self):
       print('%s eating......' %(self.name))
   def sleep(self):
       # 獲取對象self封裝的屬性(name, age, gender)
       print('%s sleep.......' %(self.name))
# 2). 實(shí)例化: 通過類實(shí)現(xiàn)對象的創(chuàng)建
xiaoming = People("小明", 20, '男')
# 將對象self/xiaoming封裝的屬性(name, age, gender)從里面拿出來;
print(xiaoming.name)
xiaoming.eat()
xiaoming.sleep()
xiaohong = People("小紅", 20, '女')
print(xiaohong.name)
xiaohong.eat()
xiaohong.sleep()

2、繼承特性

1)繼承

繼承描述的是事物之間的所屬關(guān)系,當(dāng)我們定義一個(gè) class 的時(shí)候,可以從某個(gè)現(xiàn)有的 class 繼承,新的 class 稱為子類、擴(kuò)展類(Subclass),而被繼承的class稱為基類、父類或超類(Baseclass、Superclass)。

問題一:如何實(shí)現(xiàn)繼承呢?

子類在繼承的時(shí)候,在定義類時(shí),小括號( )中為父類的名字,例如: class son(father): 這里father就是son的父類。

問題二:繼承的工作機(jī)制是什么?

父類的屬性、方法,會被繼承給子類。 舉例如下:如果子類沒有定義 init 方法,父類有,那么在子類繼承父類的時(shí)候這個(gè)方法就被繼承了,所以只要?jiǎng)?chuàng)建對象,就默認(rèn)執(zhí)行了那個(gè)繼承過來的 init 方法。

重寫父類方法:就是在子類中,有一個(gè)和父類相同名字的方法,那么在子類中的方法就會覆蓋掉父類中同名的方法,就實(shí)現(xiàn)了對父類方法的重寫。

調(diào)用父類的方法:

  1. 在子類中,直接利用 父類名.父類的方法名()
  2. super() 方法: python2.2+的功能,格式為: super(子類名稱, self).父類的方法名() (建議用此方法)

2)多繼承

多繼承,即子類有多個(gè)父類,并且具有它們的特征。

新式類與經(jīng)典類的區(qū)別:

在 Python 2及以前的版本中,由任意內(nèi)置類型派生出的類,都屬于“新式類”,都會獲得所有“新式類”的特性;反之,即不由任意內(nèi)置類型派生出的類,則稱之為“經(jīng)典類”。

新式類:

class 類名(object):

pass

經(jīng)典類:

class 類名:

pass

“新式類”和“經(jīng)典類”的區(qū)分在 Python 3之后就已經(jīng)不存在,在 Python 3.x之后的版本,因?yàn)樗械念惗寂缮詢?nèi)置類型 object(即使沒有顯示的繼承 object 類型),即所有的類都是“新式類”。

它們兩個(gè)最明顯的區(qū)別在于繼承搜索的順序不同,即:

經(jīng)典類多繼承搜索順序(深度優(yōu)先算法):先深入繼承樹左側(cè)查找,然后再返回,開始查找右側(cè)。

新式類多繼承搜索順序(廣度優(yōu)先算法):先在水平方向查找,然后再向上查找。

圖示如下:

微信截圖_20210202092819

具體例子如下:

# python2:
    # 經(jīng)典類:  class Father:
    # 新式類:   class Father(object):
# python3:
#       所有的都是新式類
# 新式類:  廣度優(yōu)先
# 經(jīng)典類:  深度優(yōu)先
class D:
    a = 'd'
class B(D):
    pass
class C(D):
    a = 'c'
class A(B, C):
    pass
obj = A()
print(obj.a)

運(yùn)行結(jié)果是 c,由此可見為廣度優(yōu)先搜索的結(jié)果。

3)私有屬性與私有方法

默認(rèn)情況下,屬性在 Python 中都是“public”, 大多數(shù) OO (面向?qū)ο螅┱Z言提供“訪問控制符”來限定成員函數(shù)的訪問。

在 Python 中,實(shí)例的變量名如果以 __ (雙下劃線)開頭,就變成了一個(gè)私有變量/屬性(private),實(shí)例的函數(shù)名如果以 __ 開頭,就變成了一個(gè)私有函數(shù)/方法(private)只有內(nèi)部可以訪問,外部不能訪問。

那么私有屬性一定不能從外部訪問嗎?

python2版本不能直接訪問 屬性名,是因?yàn)?Python 解釋器對外把 屬性名 改成了 _類名屬性名 ,所以,仍然可以通過 _類名屬性名 來訪問 屬性名 。 但是不同版本的 Python 解釋器可能會把 屬性名 改成不同的變量名,因此不建議用此類方法訪問私有屬性。

私有屬性和方法的優(yōu)勢:

  1. 確保了外部代碼不能隨意修改對象內(nèi)部的狀態(tài),這樣通過訪問限制的保護(hù),代碼更加健壯。
  2. 如果有要允許外部代碼修改屬性怎么辦?可以給類增加專門設(shè)置屬性方法。 為什么大費(fèi)周折?因?yàn)樵诜椒ㄖ校梢詫?shù)做檢查,避免傳入無效的參數(shù)。

具體實(shí)例如下:

class Student(object):
    __country = 'china'
    def __init__(self, name, age, score):
        self.name = name
        # 年齡和分?jǐn)?shù)是私有屬性
        self.__age = age
        self.__score = score
    # 私有方法, 只能在類內(nèi)部執(zhí)行;
    def __modify_score(self, scores):
        self.__score = scores
        print(self.__score)
    def set_age(self, age):
        if 0<age <150:
            self.__age = age
            print("當(dāng)前年齡:", self.__age)
        else:
            raise  Exception("年齡錯(cuò)誤")
    def set_score(self, scores):
        if len(scores) == 3:
            self.__score = scores
        else:
            raise Exception("成績異常")
class MathStudent(Student):
    pass
student = Student("Tom", 10, [100, 100, 100])
# 在類的外部是不能直接訪問的;
print(student.name)
student.set_age(15)
# Python 解釋器對外把  __屬性名 改成了  _類名__屬性名
# print(student._Student__age)

3.多態(tài)特性

多態(tài)(Polymorphism)按字面的意思就是“多種狀態(tài)”。在面向?qū)ο笳Z言中,接口的多種不同的實(shí)現(xiàn)方式即為多態(tài)。通俗來說: 同一操作作用于不同的對象,可以有不同的解釋,產(chǎn)生不同的執(zhí)行結(jié)果。

多態(tài)的好處就是,當(dāng)我們需要傳入更多的子類,只需要繼承父類就可以了,而方法既可以直接不重寫(即使用父類的),也可以重寫一個(gè)特有的。這就是多態(tài)的意思。調(diào)用方只管調(diào)用,不管細(xì)節(jié),而當(dāng)我們新增一種的子類時(shí),只要確保新方法編寫正確,而不用管原來的代碼。這就是著名的“開放封閉”原則:

  • 對擴(kuò)展開放(Open for extension):允許子類重寫方法函數(shù)
  • 對修改封閉(Closed for modification):不重寫,直接繼承父類方法函數(shù)


0 人點(diǎn)贊