# 程式碼模組化與應用 ## 讓程式碼更容易被維護、除錯的方法 模組化可以想成許多的積木,可以快速被取用、組合成各種物品。 隨著程式碼越來越龐大複雜,設計程式時也應該遵循這樣的設計原則,包括: - 執行程序模組化:使用`函式(Function)` - 資料格式模組化:使用`物件(Object)`與`類別(Class)` - 系統檔案模組化:使用`函式庫(Library)`、`套件(Package)`、`模組(Module)`與`應用程式介面(API)` 以上設計原則,都是為了程式可以更容易被維護、除錯。 ## 執行程序模組化:函式(Function) 在Python中,我們使用`函式(Function)`將重複出現的程式碼,通常是一系列的程序,做成函式,可以隨時呼叫使用,增加開發效率、以及讓程式碼更容易維護。 主要好處如下: * 減少撰寫重覆的程式碼 * 將程式碼以有意義的方式組織起來 * 相同的流程下,可藉由參數調整程式的行為 * 藉由函式庫可組織和分享程式碼 * 做為資料結構 (data structures) 和物件 (objects) 的基礎 ### 函式的宣告 函式包含以下部分: * 函式的名稱 (identifier) * 函式的參數 (parameters),相當於輸入 * 函式的回傳值 (return value),相當於輸出 * 函式的本體 (body) #### 函式的類型 函式分成兩種類型,有`回傳值`與`無回傳值`。 請參考以下範例程式碼: ```python= # 無回傳值 def function1(): print("Hello") # 有回傳值 def function2(): a = 0 a += 1 return a #呼叫function 1 function1() #呼叫function 2 print(function2()) ``` #### 函式的參數 函式可以帶入`參數`,在函式內使用該參數進行運算,但也可以不帶入參數。 ```python= # 無帶入參數 def function1(): a = 0 b = 0 a += 1 b = a return b # 有帶入參數 def function2(a): b = 0 a += 1 b = a return b # 呼叫function 1 print(function1()) # 呼叫function 2 x = 2 print(function2(x)) ``` ### <a id="var_scope"></a>變數範圍(Variable Scope) 在函式裡宣告使用的變數,與函式外的變數,有不同的使用範圍限制,我們稱為`Variable Scope`。 分成兩種: - `廣域範圍(global scope)` 或稱module scope,範圍是單個檔案(*.py)。 模組(或app)是層層堆疊起來的,並不會說哪裡才是真正的global環境,要說的話,最接近的可能就是built-in的變數如True、None所宣告的地方吧 - `區域範圍(local scope)` 以函式function為範圍,scope伴隨函式被呼叫時建立,變數重新綁定 可以透過函式`globals()`、`locals()`輸出該scope的所有變數 ### <a id="recursion"></a>函式進階使用技巧:遞迴 `遞迴(Recursion)`,定義如下。 ``` 一個函式,由自身定義或呼叫,且具備以下兩個條件,就叫做遞迴。 1. 可以反覆執行的過程 2. 跳出執行過程的出口 ``` 根據呼叫對象不同,遞迴可分成兩種 1. 直接遞迴:Function自身呼叫自己。 ```python= def Func(...): ... if(...) Func(...) ... ``` 2. 間接遞迴:Function之間互相呼叫。 ```python= def Func1(): ... if(...) Func2() ... def Func2(): ... if(...) Func1() ... ``` ### 隨堂練習 1. 階乘的計算 階乘的基本定義如下 - n == 0, n! = 1 - n > 0, n! = n x (n-1)!, n正整數 請利用遞迴的技巧設計程式,計算 **4!** 為何? 參考程式碼如下: ```python= def factorial(n): if n == 0: return 1 else: return( n * factorial(n-1)) print(factorial(4)) ``` 2. 請設計程式,請使用者輸入身高(cm)與體重(kg),並且設計兩個版本的函式來計算BMI值,名稱叫做`bmi_calculator` - 版本1. 有參數(身高與體重)、有回傳值(bmi值) - 版本2. 無參數、無回傳值 `Note: BMI計算公式:BMI=(體重/身高^2)` **參考程式碼:** 版本1. 有參數(身高與體重)、有回傳值(bmi值) ```python= def bmi_calculator(height, weight): bmi = weight/(height/100)**2 return bmi user_height = int(input("請輸入身高(cm)> ")) user_weight = int(input("請輸入體重(kg)> ")) bmi_result = bmi_calculator(user_height, user_weight) print("你的BMI是 {0:.2f}".format(bmi_result)) ``` 版本2. 無參數、無回傳值 ```python= def bmi_calculator(): user_height = int(input("請輸入身高(cm)> ")) user_weight = int(input("請輸入體重(kg)> ")) print("你的BMI是 {0:.2f}".format(bmi_result)) bmi_calculator() ``` ## 資料格式模組化:物件與類別 由於物件與類別的應用,較適合大型程式應用,例如遊戲、大型網站,在此不特別介紹,若有興趣,請參考補充資料:[物件與類別](https://hackmd.io/@howkii-studio/object_class) ## 系統檔案模組化:函式庫、套件、模組、API ![](https://i.imgur.com/58qiZGg.png) 1. `模組(Module)`:一個.py檔案就是一個 module,裡頭可以定義 `variable`, `function`, `class` 2. `套件(Package)`:一個檔案資料夾,裡面存放多個module (.py檔案) 3. `函式庫(Library)`:根據特定用途,將多個Package集合在一個資料夾中 4. `應用程式介面(API, Application Programming Interface)`:Library開發者可定義哪些module中的function可以給別人引用,這些function就稱為API。對於API引用者,不需要了解實作內容,只需要說明文件知道API用途,自行使用開發新程式。 ### 函式庫引用範例:使用第三方函式庫openpyxl 我們再回頭看這個使用第三方函式庫`openpyxl`的範例 ```python= import openpyxl import os # os.chdir 是 python 切換到電腦指定路徑的方法 os.chdir(r"/Users/chaoyen/Downloads") # 請填寫自己電腦裡Excel檔案的絕對路徑 wb = openpyxl.load_workbook('produceSales.xlsx') # 請填寫要處理的Excel檔案名稱 sheet = wb.worksheets[0] # 要更正的品名與其單價 price_updates_dict = {'Garlic': 1.99} #使用for loop掃描所有A欄品名,如果比對一致,就進行更正與上色 print("Processing...") for rowNum in range(2, sheet.max_row, 1): produceName = sheet.cell(rowNum, 1).value if produceName in price_updates_dict: sheet.cell(rowNum, 2).value = price_updates_dict[produceName] # openpyxl函式庫中的styles模組的Font API sheet.cell(rowNum, 2).font = openpyxl.styles.Font(color='FF0000') # 將結果另存新檔 wb.save('produceSales_update.xlsx') print("Done!") ``` 小提示:如果第19行想直接引用`Font`,可以在第三行新增這行程式碼 ```python= from openpyxl.styles import Font ``` ### 硬體應用API實務:Moxa UC-8100 API Library 以下內容節錄自Moxa UC-8100的[User Manual](https://www.moxa.com/en/products/industrial-computing/arm-based-computers/uc-8100-series#resources),跟購買者說明,如何引用Moxa定義的API,來開發程式,控制UC-8100這台IoT Gateway ![](https://i.imgur.com/fivj1TC.png) 有經驗的開發者看到這份手冊,就可以知道Moxa的API主要是以C語言撰寫的,在呼叫API前,要安裝相關套件。 ### 軟體應用API實務: Google Map API Library 以下內容節錄自[Google Maps Platform](https://cloud.google.com/maps-platform/?hl=zh&utm_source=google&utm_medium=cpc&utm_campaign=FY18-Q2-global-demandgen-paidsearchonnetworkhouseads-cs-maps_contactsal_saf&utm_content=text-ad-none-none-DEV_c-CRE_467208339263-ADGP_Hybrid%20%7C%20AW%20SEM%20%7C%20BKWS%20~%20Brand%20%7C%20EXA%20%7C%20Google%20Maps%20API-KWID_43700057416638020-aud-596763661393%3Akwd-401469081526-userloc_9073389&utm_term=KW_google%20map%20api-ST_Google%20Map%20API&gclid=Cj0KCQiAhs79BRD0ARIsAC6XpaWYRBX80JE9KdV-TgJWHryK-JRvcFf-5jeQwNm7foY8VWd8W--J2iQaArC3EALw_wcB),這裡提供給想要使用Google Map API來做應用的開發者 ![](https://i.imgur.com/raeZ2cD.png) 有經驗的開發者看到這個網站的說明,就可以知道Google Map的API主要是以Javascript撰寫的。 ### 隨堂練習 請設計大樂透的報明牌程式,從1~49裡面,隨機抽出六個不重複的數字 請引用`random.sample`這個模組來隨機取出六個數字 **不直接引用sample API** ```python= import random num = 49 num_list = [] for i in range(0, num, 1): num_list.append(i+1) # random.sample print(f'Your lucky number: {random.sample(num_list, 6)}') ``` **直接引用sample API** ```python= from random import sample num = 49 num_list = [] for i in range(0, num, 1): num_list.append(i+1) print(f'Your lucky number: {sample(num_list, 6)}') ``` ###### tags: `Python程式設計入門`