天天看點

過程式程式設計 vs 面向對象程式設計&函數式程式設計

1.過程式程式設計

x = 2 
y = 4
z = 8
xyz = x + y + z 
xyz
           

上述代碼每個過程都改變了程式的狀态。

過程式程式設計,資料存儲在全局變量中,并通過函數處理。示例如下:

rock = []
country = []

def collect_songs():
        song = "Enter a song."
        ask = "Type r or c. q to quit"

        while True:
                genre = input(ask)
                if genre == "q":
                        break


                if genre == "r":
                        rk = input(song)
                        rock.append(rk)


                elif genre == ("c"):
                        cy = input(song)
                        country.append(cy)

                else:
                        print("Invalid.")
        print(rock)
        print(country)

collect_songs()
           

分析:由于我們将程式的狀态都存在全局變量中,如果程式慢慢變大就好碰到問題。程式規模擴大,可能會在多個函數中使用全局變量,我們很難記錄都有哪些地方對一個全局變量進行了修改。例如:某個函數可能改變了一個全局變量的值,在後面的程式中又有一個函數改變了相同的變量,因為寫第二個函數時程式忘記了已經在第一個函數中做了修改。會嚴重破壞程式的資料準确性。

弊端:程式功能變多,全局變量變多,最後随着程式規模變大需要修改變量,很快程式将無法維護。這種程式設計方式也會有副作用(side effects),其中之一就是會改變全局變量的狀态。使用過程式程式設計時,經常會碰到意料之外的副作用,比如意外遞增某個變量兩次。

2.函數式程式設計

       函數式程式設計通過消除全局狀态,解決了過程式程式設計中出現的問題。函數式程式員依靠的是不使用或不改變全局狀态的函數,他們唯一使用的狀态就是傳給函數的參數。一個函數的結果通常被繼續傳給另一個函數。是以,這些程式員通過函數之間傳遞狀态,避免了全局狀态的問題,也是以消除了由此帶來的副作用和其他問題。

       函數式代碼有一個特征:沒有副作用。它不依賴目前函數之外的資料,也不改變目前函數之外的資料。

一個帶副作用的函數。示例如下:

a = 0
def increment():
        global a
        a += 1
        print(a)


#測試代碼:
increment() #傳回1
print(a)  #傳回1
           

一個不帶副作用的函數,示例如下:

def increment(a):
        return a + 1

#測試代碼:
print(increment(2))
           

       第一個函數有副作用,因為它依賴函數之外的資料,并改變了目前函數之外的資料—遞增了全局變量的值。第二個函數沒有副作用,因為它沒有依賴或修改自身之外的資料。

       函數式程式設計的一個優點,在于它消除了所有由全局狀态引發的錯誤(函數式程式設計中不存在全局狀态)。但是也有缺點,即部分問題更容易通過狀态進行概念化。例如,設計一個包含全局狀态的人機界面,比設計沒有全局狀态的人機界面要更簡單。如果你要寫一個程式,通過按鈕改變使用者看到畫面的可見狀态,用全局狀态來編寫該按鈕會更簡單。你可以建立一個全局變量,值為True 時畫面可見,值為False 時則不可見。如果不使用全局狀态,設計起來就比較困難。

3.面向對象程式設計

       面向對象(object-oriented)程式設計範式也是通過消除全局狀态來解決過程式程式設計引發的問題,但并不是用函數,而是用對象來儲存狀态。在面向對象程式設計中,類(class)定義了一系列互相之間可進行互動的對象。類是程式員對類似對象進行分類分組的一種手段。假設有一袋橘子,每個橘子是一個對象。所有的橘子都有類似的屬性,如顔色和重量,但是這些屬性的具體值則各不相同。這裡就可以使用類對橘子進行模組化,建立屬性值不同的橘子對象。例如,可定義一個橘子類,既支援建立深色、重10 盎司(約283克)的橘子對象,也支援建立淺色、重12 盎司(約340 克)的橘子對象。每個對象都是類的示例。如果定義了一個叫Orange 的類,然後建立兩個Orange 對象,那麼每個對象都是Orange 類的執行個體;它們的資料類型相同,都是Orange。對象和執行個體這兩個術語可以替換使用。在定義類時,該類的所有執行個體是類似的:都擁有類中定義的屬性,如顔色或種類,但是每個執行個體的具體屬性值是不一樣的。

       在Python 中,類是一個複合語句,包含代碼頭和主體。可使用文法class [類名]:[代碼主體]來定義類,其中[類名]是類的名稱,[代碼主體]是定義的類的具體代碼。根據慣例,Python 中的類名都是以大寫字母開頭,且采用駝峰命名法,即如果類名由多個單詞組成,每個單詞的第一個字母都應該大寫,如LikeThis,而不是用下劃線分隔(函數的指令慣例)。類中的代碼主體可以是一個單一語句,或一個叫方法(method)的複合語句。方法類似于函數,但因為是在類中定義的,隻能在類建立的對象上調用方法(如本書第一部分中在字元串上調用.upper())。方法的命名則遵循函數命名規則,都是小寫,并用下劃線分隔。

        方法的定義方式與函數定義方式相同,但有兩處差別:一是必須在類的内部定義方法,二是必須接受至少一個參數(特殊情況除外)。按照慣例,方法的第一個參數總是被命名為self。建立方法時,至少要定義一個參數,因為在對象上調用方法時,Python會自動将調用方法的對象作為參數傳入。示例如下:

class Orange():
        def __init__(self,w,c):
                self.weght = w
                self.color = c
                print("Created!")
           

        可使用self 定義執行個體變量(instance variable):屬于對象的變量。如果建立多個對象,各自都有不同的執行個體變量值。通過文法self.[變量名] = [變量值]定義執行個體變量。通常是在特殊方法__init__(代表初始化)中定義執行個體變量,建立對象時Python 會調

用該方法。

       建立Orange 對象時(上例中沒有建立),就會執行__init__中的代碼,建立兩個執行個體變量:weight 和color。可以在類的方法中,和使用普通變量一樣使用這些執行個體變量。建立Orange 對象時,__init__中的代碼還會列印Created!。雙下劃線包圍的方法(如__init__),被稱為魔法方法(magic method),即Python 用于建立對象等特殊用途的方法。

       建立新Orange 對象的文法,與調用函數的文法類似:[類名]([參數]),将[類名]替換為想要用來建立對象的類的名稱,[參數]替換為__init__接受的參數即可。這裡不用傳入self 參數,Python 會自動傳入。建立新對象的過程,也被稱為建立類的例。 Python告訴我們它是一個Orange 對象,并列印其在記憶體中的位址(你在計算機上運作時得到的結果,将不同于本例中的結果)。

class Orange():
        def __init__(self,w,c):
                self.weght = w
                self.color = c
                print("Created!")

orl = Orange(10,"dark")
print(orl)
#傳回:
#Created!
#<__main__.Orange object at 0x02B30490>    


print(orl.weght)
print(orl.color) 


#也可使用文法[對象名].[變量名] = [新的值]改變執行個體變量的值:
orl.weght = 100
orl..color = yellow

print(orl.weght)
print(orl.color)     

#可以使用Orange 類建立多個橘子對象
orl2 = Orange(23,"white")
orl3 = Orange(12,"red")      
           

 #還有其他的屬性。它可能還會腐爛,這些都可以通過方法來模拟。下面的代碼為Orange 對象增加了腐爛的屬性:

class Orange():
        def __init__(self,w,c):
                self.weght = w
                self.color = c
                self.mold = 0
                print("Created!")

        def rot(self,days,temp):
                self.mold = days * temp
                

orl = Orange(7,"dark")
print(orl.mold)
orl.rot(10,98)
print(orl.mold)
#rot 方法接受兩個參數:橘子被摘下的天數,以及這些天的平均溫度。調用該方法時,
#其使用公式改變了變mold 的值,這是因為可以在任意方法中改變執行個體變量的值。現在,橘子就可以腐爛了
           

       面向對象程式設計有多個優點:鼓勵代碼複用,進而減少了開發和維護的時間;還鼓勵拆解問題,使代碼更容易維護。但有一個缺點便是編寫程式時要多下些功夫,因為要做很多的事前規劃和設計。

習題:

1.定義一個叫Apple 的類,建立4 個執行個體變量,表示蘋果的4 種屬性。

2.定義一個叫Circle 的類,建立area 方法計算其面積。然後建立一個Circle對象,調用其area 方法,列印結果。可使用Python 内置的math 子產品中的pi 函數。

3.定義一個叫Triangle 的類,建立area 方法計算并傳回面積。然後建立一個Triangle 對象,調用其area 方法,列印結果。

4.定義一個叫Hexagon 的類,建立cacculate_perimeter 方法,計算并傳回其周長。然後建立一個Hexagon 對象,調用其calculate_perimeter 方法,并列印結果。