從單一程式走向模組、套件與可執行腳本
把智慧養殖巡查程式拆成可重用、可測試,也能由現場人員直接執行的工具。
完成本篇後,你能說明模組、套件與腳本的差異;使用不同形式的
import;以 __name__ == "__main__" 區分匯入與執行;並把智慧養殖巡查程式整理成小型專案。一、程式為什麼要「分家」?
初學時,把輸入、計算與輸出寫在同一個檔案很方便。但當程式開始讀取多座養殖場資料、判斷水質、輸出巡查報告時,單一檔案會愈來愈難修改。合理拆分後,判斷規則可以重複使用,主程式也能專注描述工作流程。
| 名稱 | 在 Python 中的意義 | 本篇案例 |
|---|---|---|
| 模組 module | 一個 .py 程式檔 | water_quality.py |
| 套件 package | 收納相關模組的資料夾 | shuijing_monitor/ |
| 腳本 script | 由命令列或編輯器直接執行的程式 | run_patrol.py |
二、先把判斷函式做成模組
建立 water_quality.py。模組只負責水質資料的判斷與摘要,不在匯入時向使用者詢問資料。
"""水井村智慧養殖:水質判斷工具。"""
TEMPERATURE_RANGE = (24.0, 30.0)
PH_RANGE = (6.5, 8.5)
def in_range(value, safe_range):
"""判斷數值是否落在含端點的安全範圍。"""
minimum, maximum = safe_range
return minimum <= value <= maximum
def check_water(temperature, ph):
"""回傳水質檢查結果串列;空串列表示未發現異常。"""
alerts = []
if not in_range(temperature, TEMPERATURE_RANGE):
alerts.append(f"水溫需確認:{temperature}°C")
if not in_range(ph, PH_RANGE):
alerts.append(f"pH 值需確認:{ph}")
return alerts
def format_report(site_name, temperature, ph):
"""產生單一場域的文字報告。"""
alerts = check_water(temperature, ph)
status = "正常" if not alerts else ";".join(alerts)
return f"{site_name}|水溫 {temperature}°C|pH {ph}|{status}"三、用 import 重複使用模組
把 patrol_demo.py 與 water_quality.py 放在同一資料夾,便能匯入模組。
import water_quality
report = water_quality.format_report("示範池 A", 27.2, 7.4)
print(report)使用「模組名稱+點」能清楚看出函式來自哪裡。另有兩種常見寫法:
from water_quality import format_report
print(format_report("示範池 B", 31.1, 7.8))import water_quality as wq
print(wq.format_report("示範池 C", 25.6, 6.2))from 模組 import *,因為名稱來源不明,也可能覆蓋原有變數或函式。四、認識 __name__ 與程式入口
Python 直接執行檔案時,該檔案的 __name__ 是 "__main__";被其他程式匯入時,則是模組名稱。利用這項規則,可讓測試示範只在直接執行時出現。
def main():
print(format_report("模組自我測試池", 27.0, 7.2))
if __name__ == "__main__":
main()將這段放在 water_quality.py 最後。直接執行模組會看到測試報告;其他程式只匯入函式時,不會自動印出內容。
五、從模組組成套件
當功能再增加,可把相關模組放入同一套件。建議專案結構如下:
__init__.py 可保持空白,也能公開最常使用的函式:
"""水井村智慧養殖巡查工具套件。"""
from .water_quality import check_water, format_report
__all__ = ["check_water", "format_report"]其中的點代表「目前套件」。設定 __all__ 是在說明套件預計提供哪些名稱,但不等同存取權限或安全機制。
六、套件內的相對匯入
新增 report.py,把多筆場域資料整理成巡查報告:
from .water_quality import format_report
def build_patrol_report(records):
"""將場域紀錄轉成多行巡查報告。"""
lines = ["水井村智慧養殖巡查報告", "=" * 24]
for record in records:
lines.append(
format_report(
record["場域"],
record["水溫"],
record["pH"],
)
)
return "\n".join(lines)七、完成可直接執行的巡查腳本
根目錄的 run_patrol.py 是整個專案入口。它準備資料、呼叫套件函式並顯示結果。
from shuijing_monitor.report import build_patrol_report
def main():
records = [
{"場域": "示範池 A", "水溫": 27.2, "pH": 7.4},
{"場域": "示範池 B", "水溫": 31.1, "pH": 7.8},
{"場域": "示範池 C", "水溫": 25.6, "pH": 6.2},
]
print(build_patrol_report(records))
if __name__ == "__main__":
main()在終端機切換到 shuijing_project 後執行:
python run_patrol.py八、標準函式庫與第三方套件不同
| 來源 | 例子 | 是否另行安裝 |
|---|---|---|
| 自己撰寫 | shuijing_monitor | 同一專案中通常不需要 |
| Python 標準函式庫 | csv、json、pathlib | 隨 Python 提供 |
| 第三方套件 | requests、pandas | 通常使用 pip 安裝 |
安裝第三方套件時,建議在專案的虛擬環境中執行,例如:
python -m pip install requests使用 python -m pip 能較明確地讓 pip 對應到目前使用的 Python。
九、匯入時 Python 去哪裡找?
Python 會從目前程式所在位置、環境設定與已安裝套件路徑中尋找模組。遇到 ModuleNotFoundError 時,先檢查:
- 檔名與匯入名稱是否一致,大小寫是否正確。
- 是否從專案根目錄執行主程式。
- 模組檔名是否誤用
csv.py、json.py等標準模組名稱。 - Thonny、終端機與 Colab 是否使用不同的 Python 環境。
十、常見錯誤與修正
| 現象 | 原因 | 修正 |
|---|---|---|
| 一 import 就開始詢問輸入 | 互動程式寫在模組最外層 | 放入 main() 與入口判斷 |
| ModuleNotFoundError | 執行位置、檔名或環境錯誤 | 由專案根目錄執行並確認解譯器 |
| AttributeError | 函式名稱寫錯或循環匯入 | 確認公開名稱並重新分配模組責任 |
| 同一段程式重複多份 | 沒有抽成共同模組 | 把計算規則集中,其他程式只匯入 |
| 匯入自己的 csv.py 出錯 | 檔名遮蔽標準函式庫 | 重新命名並清除舊快取 |
十一、USR 實作挑戰
把巡查程式分成「感測資料驗證」、「異常判斷」與「報告輸出」三個模組,為每個模組寫一句用途說明。
把警示範圍移到設定模組,但不要自行假設真實數值;以註解標示「須由養殖戶與專業人員確認」。
由三位同學分別維護資料、判斷與報告模組,最後以主腳本整合,記錄匯入介面曾發生的修改。
十二、與生成式 AI 協作
請擔任 Python 專案重構助教。
請檢查我提供的智慧養殖程式,建議如何拆成模組、套件與主腳本。
要求:
1. 每個模組只負責一類工作;
2. 匯入時不能自動執行 input 或列印;
3. 主程式使用 if __name__ == "__main__";
4. 不要自行設定真實養殖安全門檻;
5. 說明每項調整的理由,再提供檔案樹與程式碼。模組讓函式可以重用,套件讓多個模組形成清楚的功能群組,腳本則提供可直接執行的入口。良好的拆分不以檔案數量為目標,而是讓每個檔案的責任容易說明、測試與修改。
沒有留言:
張貼留言