Python的包裝機制 — Function, Class, Module, Package

Young Chen
宅男雜誌
Published in
5 min readJan 18, 2019
看別人低品質程式碼就是這種感覺…

由於程式規模越寫越大,如何整理得當使得程式碼拆分整併成好讀又易用是工程前期的重點,二來也可以讓下次改版時不會改得更痛苦,維持高維護性。Python很好寫,但是未經過整理還是一團亂,失去了Python要讓任何事情直覺又簡單的禪意,最近接手一團亂的程式碼剛好需要接觸這個議題,筆記一下理解心得之後運用。

函式 Function

通常,當發現程式碼片段相似只有參數會變化時,這時可以考慮將程式碼包成函數使用,Python的函數以def開頭作為宣告:

def add(a, b):
return a+b
print(add(1, 2))

嘗試將簡單的加法包裝成函數,除了可以提高重用程度外,也是一種抽象化的實作,將執行的細節(加法這動作)隱藏起來。

模組 Module

假設我正在做一個兩數運算的簡易數學模組,增加一組數學相關函數存成一個檔案命名為math_module.py,即完成一個模組。這也是Python裡面最直接直覺的一個建立模式:

# Save as math_module.pydef add(a, b):
return a+b
def sub(a, b):
return a-b
def 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 m2
a1 = m1.Math(1, 2)
a2 = m2.Math2(3, 4)
a1.add()
a2.times()
print(a1)
print(a2)
'''
Answer: 3
Answer: 12
'''

以上範例實現的專案規模從小到大的整理方式,希望這自己寫的荒謬例子(笑)可以幫助你理解Python的包裝架構。

--

--

Young Chen
宅男雜誌

曾經是全端工程師,目前在資料科學團隊中主要負責雲端架構相關工作,透過自學正在資料科學領域相關知識耕耘中。mail: chiyoung0307@gmail.com