Python的包裝機制 — Function, Class, Module, Package
由於程式規模越寫越大,如何整理得當使得程式碼拆分整併成好讀又易用是工程前期的重點,二來也可以讓下次改版時不會改得更痛苦,維持高維護性。Python很好寫,但是未經過整理還是一團亂,失去了Python要讓任何事情直覺又簡單的禪意,最近接手一團亂的程式碼剛好需要接觸這個議題,筆記一下理解心得之後運用。
函式 Function
通常,當發現程式碼片段相似只有參數會變化時,這時可以考慮將程式碼包成函數使用,Python的函數以def開頭作為宣告:
def add(a, b):
return a+bprint(add(1, 2))
嘗試將簡單的加法包裝成函數,除了可以提高重用程度外,也是一種抽象化的實作,將執行的細節(加法這動作)隱藏起來。
模組 Module
假設我正在做一個兩數運算的簡易數學模組,增加一組數學相關函數存成一個檔案命名為math_module.py,即完成一個模組。這也是Python裡面最直接直覺的一個建立模式:
# Save as math_module.pydef add(a, b):
return a+bdef sub(a, b):
return a-bdef p(content):
return print(content)p(add(1,2)) #3
p(sub(3,4)) #-1
相同目錄下建立一個test_math.py當作測試模組,這時可以用關鍵字import將模組載入並執行,達成模組化還有重用程式碼的效果,如此一來其他程式需要兩數運算時可以載入該模組。
#test_math.pyimport math_module as mprint(m.add(1,2)) #3
m.p(m.sub(2, 3)) #-1
類別 Class
什麼時候該用類別?有此一說,當將某些狀態與功能黏在一起時,適合用類別。其實真的有點抽象,過去寫Java時其實也沒有真正仔細思考的問題,畢竟起手式就一定要class似乎也沒得選,但是Python基本上沒有Class一樣可以執行使用,那麼真正的價值在哪?多數主流的答案可能會是可實現物件導向設計,而物件導向在嘗試解決的其實是易用性(Usability)與重用性(Reusability)。白話點,以後比較好用,也可以讓程式碼一直被重複使用。假想世界上數學這件事情只存在加減法與兩個整數,這就是數學(寫例子方便啦),我們可能可以這樣設計類別:
class Math(): def __init__(self, a, b):
self.a = a
self.b = b
self.answer = 0
def add(self):
self.answer = self.a + self.b
#return self.answer
def sub(self):
self.answer = self.a-self.b
#return self.answer
def __str__(self):
return 'Answer: {0}'.format(self.answer)mathObj = Math(1, 2)mathObj.add()print(mathObj)
這樣一個只有兩數加減法的數學模組就完成了(數學要是只有這樣多單純),我承認這例子舉的不算好,但為了延續性,依然用加減法舉例。另外這也可以觀察到一點,也是最近工作的心得:OOP的重點依然是考量到未來的擴展與嘗試讓複雜的系統重用/易用提高,以這個例子而言,其實OOP的優點完全沒有感受到,這樣需要嗎?我的想法是:如果只是簡單的任務,為何你還要用OOP?
套件 Package
當一堆模組(一堆.py檔案)分別分工實現某些功能的時候,當彼此有共通性成為一種類別後,應該要怎麼管理?這時套件就可以利用上了!亂成一團的py檔案加上可能的檔案名稱衝突,沒利用一個資料夾整理會讓架構顯凌亂。想像乘法與除法至另外一個數學世界,姑且取個代號2在myMath資料夾中,管理所有跟數學相關的套件,架構如下:
main.py是利用數學套件的主程式,而myMath資料夾嘗試聚合與數學相關的模組成為一個套件,需要注意的是,要建立一個__init__.py檔(通常是空白),達到套件化。以下分別為各檔案程式碼:
#math_module_2.pyclass Math2():
def __init__(self, a, b):
self.a = a
self.b = b
self.answer = 0
def times(self):
self.answer = self.a * self.b def div(self):
if self.b == 0:
raise ValueError('cannot div 0')
else:
self.answer = self.a / self.b
def __str__(self):
return 'Answer: {0}'.format(self.answer)
以下是主程式內容:
#main.pyimport myMath.math_module as m1
import myMath.math_module_2 as m2a1 = m1.Math(1, 2)
a2 = m2.Math2(3, 4)a1.add()
a2.times()print(a1)
print(a2)'''
Answer: 3
Answer: 12
'''
以上範例實現的專案規模從小到大的整理方式,希望這自己寫的荒謬例子(笑)可以幫助你理解Python的包裝架構。