Python 模組與套件:程式碼組織與重用 (第 5 章)

站主自己的課程,請大家支持
揭秘站長的架站心法:如何利用 Hugo × AI 打造高質感個人品牌網站? 揭秘站長的架站心法:如何利用 Hugo × AI 打造高質感個人品牌網站?
  • Post by
  • Dec 19, 2023
post-thumb

當程式碼從幾十行增長到幾千行時,將所有邏輯塞在一個 .py 檔案中是一場災難。模組化 (Modularity) 是軟體工程的核心原則。透過將程式碼拆解成獨立的模組 (Modules)套件 (Packages),我們能提升程式碼的可讀性、可維護性與重用性。

本章將帶您掌握 Python 的引用機制,並探索強大的內建標準函式庫 (Standard Library)。

1. 模組 (Modules):單一檔案的封裝

在 Python 中,任何一個 .py 檔案都是一個模組。模組名稱就是檔案名稱 (不含副檔名)。

假設我們有一個檔案 math_utils.py

# math_utils.py
PI = 3.14159

def add(a, b):
    return a + b

def sub(a, b):
    return a - b

1.1 導入模組的四種方式

# 1. 導入整個模組 (推薦,保留命名空間)
import math_utils
print(math_utils.add(10, 5))

# 2. 導入特定函數 (方便,但須注意命名衝突)
from math_utils import add, PI
print(add(10, 5))

# 3. 導入並取別名 (Alias) (常用於長名稱模組)
import math_utils as mu
print(mu.PI)

# 4. 導入所有內容 (不推薦!容易汙染命名空間)
from math_utils import *

1.2 模組搜尋路徑 (The Module Search Path)

當你輸入 import xxx 時,Python 會依照 sys.path 列表中的順序搜尋:

  1. 當前目錄。
  2. PYTHONPATH 環境變數目錄。
  3. 標準函式庫安裝目錄。
import sys
print(sys.path)  # 查看搜尋路徑

2. 套件 (Packages):模組的集合

當模組數量變多時,我們需要用資料夾來組織它們。套件本質上就是一個包含 __init__.py 檔案的資料夾。

注意:自 Python 3.3 起,__init__.py 已非強制,但為了支援舊版及明確定義套件屬性,建議保留。

專案結構範例

my_project/
├── main.py
└── utils/              <-- 套件 (Package)
    ├── __init__.py     <-- 標識這是一個套件
    ├── file_ops.py     <-- 子模組 module
    └── net_ops.py      <-- 子模組 module

__init__.py 的作用

  1. 標識套件:告诉 Python 這個目錄可以被 import。
  2. 初始化程式碼:import 套件時會優先執行此檔案。
  3. 控制導出:可以在此定義 __all__ 列表,控制 from package import * 時導出的模組。
# utils/__init__.py
from .file_ops import read_file  # 方便使用者直接从 utils 導入

main.py 中使用:

import utils
utils.read_file("data.txt")  # 透過 __init__.py 轉發

3. Python 標準函式庫巡禮 (Battery Included)

Python 以 “Batteries Included” (電池已隨附) 著稱,內建了極其豐富的函式庫。

模組用途範例
os作業系統介面os.listdir(), os.path.join()
sys系統參數與路徑sys.argv (命令列參數), sys.exit()
math數學運算math.sqrt(), math.sin()
datetime日期與時間datetime.now(), timedelta()
jsonJSON 處理json.loads(), json.dumps()
random隨機數生成random.choice(), random.randint()

實戰:使用 osdatetime 備份檔案

import os
import shutil
from datetime import datetime

def backup_file(filename):
    if not os.path.exists(filename):
        print("檔案不存在!")
        return

    # 產生時間戳記
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    backup_name = f"{filename}_{timestamp}.bak"

    shutil.copy(filename, backup_name)
    print(f"備份成功:{backup_name}")

4. 關鍵慣用寫法:if __name__ == "__main__":

這是 Python 中最常見的樣板程式碼。它用來判斷模組是被直接執行還是被導入

  • 直接執行檔案時:__name__ 的值為 "__main__"
  • 被 import 時:__name__ 的值為模組名稱 (如 "math_utils")。
# powerful_script.py

def main():
    print("正在執行主程式邏輯...")

if __name__ == "__main__":
    # 只有當使用者在終端機輸入 `python powerful_script.py` 時才會執行
    main()

這讓我們可以撰寫既能作為獨立腳本執行,又能作為模組被別人導入的程式碼(方便單元測試)。

5. 第三方套件與 pip

除了標準函式庫,Python 擁有全世界最大的開源生態系 (PyPI)。我們使用 pip (Python Package Installer) 來管理這些套件。

# 安裝套件 (如 requests)
pip install requests

# 升級套件
pip install --upgrade requests

# 列出已安裝套件
pip list

# 產生依賴清單 (requirements.txt)
pip freeze > requirements.txt

虛擬環境 (Virtual Environments)

為避免不同專案間的套件版本衝突,強烈建議每個專案使用獨立的虛擬環境。

# 建立虛擬環境 (一名為 venv)
python -m venv venv

# 啟用環境 (Windows)
venv\Scripts\activate

# 啟用環境 (Mac/Linux)
source venv/bin/activate

6. 常見問題 (FAQ)

Q1: import 循環引用 (Circular Import) 怎麼辦?

A: 當模組 A 引用 B,且 B 也引用 A 時發生。 解決方案:

  1. 重新架構程式碼,將共同依賴提取到第三個模組 C。
  2. import 語句移到函數內部(延遲導入),雖然不推薦但在緊急時有效。

Q2: 為什麼找不到我的模組?(ModuleNotFoundError)

A: 檢查 sys.path。Python 只會搜尋路徑列表中的目錄。如果你的模組在子目錄且沒被視為套件,可能需要手動將該目錄加入 sys.path,或者正確設置 PYTHONPATH。

7. 總結

模組化是從寫程式 (Scripting) 進階到軟體工程 (Engineering) 的關鍵一步。

本章重點回顧:

  • Module: 單一 .py 檔案。
  • Package: 包含 __init__.py 的資料夾。
  • Import: 推薦使用 import modulefrom module import item,避免 import *
  • Standard Library: 善用 os, sys, json 等內建模組。
  • Entry Point: 使用 if __name__ == "__main__": 控制執行入口。

在掌握了基礎語法與結構後,下一章我們將進入實戰,探討 檔案處理與例外控制 (File Handling & Exceptions),學習如何與外部世界互動並處理錯誤!


延伸閱讀

LATEST POST
TAG