Day 12 模組與套件
引言
當專案開始長大,把所有程式碼塞進同一個檔案會變得難以維護。這時候把程式拆成「模組」與「套件」,讓不同檔案各自負責一部分功能,是寫大型 Python 專案的基礎。模組化之後,每個人可以專心修改自己負責的部分,別人只需要知道「這個模組提供哪些函式」,而不必從頭讀完所有程式碼。
另一方面,Python 之所以好用,是因為有著豐富的第三方套件生態系:NumPy、Pandas、Requests、Django……,只要會用 pip 就能把它們裝進自己的專案。今天就要把這兩件事一起搞懂:先學怎麼組織自己的程式碼,再用 pip 與 venv 把外部套件與環境管理好。學會模組與套件之後,你就能把過去十天寫的小工具整理成可重複使用的程式庫,也準備好迎接接下來的資料科學章節。
匯入模組:import 的各種寫法
Python 的模組其實就是一個 .py 檔案,檔案名稱就是模組名稱。例如有個 utils.py,就可以透過 import 把它載入並使用裡面的函式。最簡單的寫法是直接 import 整個模組,使用時要寫「模組名.函式名」。這種寫法雖然稍長,但能清楚看出函式來自哪個模組,閱讀性較好。
# utils.py 內有一個 greet 函式
# greet(name) 會回傳 "Hello, {name}"
import utils
print(utils.greet("小明")) # 輸出:Hello, 小明
如果只想要某幾個函式,可以用 from ... import,呼叫時就不必寫模組名。更進階的寫法還能搭配 as 替模組或函式取別名,避免名稱太長或撞名。
from utils import greet
print(greet("小華")) # 直接呼叫,不用寫 utils.
import numpy as np # 第三方套件常用別名 np
import pandas as pd # 第三方套件常用別名 pd
from math import sqrt as square_root
print(square_root(16)) # 輸出:4.0
注意 from ... import * 雖然方便,會把模組內所有公開名稱(沒有底線開頭的)全部匯入,容易與自己定義的變數衝突,正式檔案中並不推薦。建議明確列出要匯入的東西,這樣未來除錯時能快速追蹤某個函式是從哪裡來的,也方便編輯器提供自動完成。
自建模組與 __name__ 的妙用
寫一個簡單的模組很直覺:把所有函式寫進 .py 檔案,存成可重複使用的工具。Python 還提供 __name__ 這個內建變數,它會根據 .py 檔案是被「直接執行」或「被 import」自動變成 "__main__" 或模組名稱。這個機制常用來寫測試程式碼,讓同一支程式既能當工具也能自我驗證。
# 檔案:calculator.py
def add(a, b):
return a + b
def subtract(a, b):
return a - b
if __name__ == "__main__":
# 這段只在自己執行 python calculator.py 時會跑
print("測試加法:", add(3, 5))
print("測試減法:", subtract(10, 4))
當其他模組用 import calculator 載入時,底下那兩行 print 不會執行;但直接用 python calculator.py 執行時,就會跑測試。這是 Python 專案裡很常見的寫法,能讓同一個檔案兼顧「可被引用」與「可單獨測試」兩種用途。如果想寫更完整的測試,則可以導入 pytest 或 unittest 模組,這在後續開發資料處理工具時會很有用。
套件結構:把多個模組組織起來
當模組數量變多,把相關的 .py 檔案放進同一個資料夾,這個資料夾就稱為「套件」。套件需要一個特殊的 __init__.py 檔案,Python 看到它才會把資料夾當成套件處理(從 Python 3.3 起,名稱空間套件可省略,但實務上仍然建議保留)。__init__.py 在套件被首次載入時會自動執行,是初始化套件狀態的好地方。
# 專案結構
# my_project/
# ├── main.py
# └── mylib/
# ├── __init__.py
# ├── math_tools.py
# └── string_tools.py
# mylib/math_tools.py
def double(x):
return x * 2
# mylib/__init__.py
from .math_tools import double
# main.py
from mylib import double
print(double("main")) # 輸出:mainmain
__init__.py 除了標示這是套件之外,也可以在裡面做「匯出哪些名稱」的設定,避免呼叫端看到太多內部細節。實務上常見的做法是把套件常用的 API 都寫在 __init__.py,讓使用者只要 import 套件就能用,例如 from mylib import double 而不用寫 from mylib.math_tools import double。子模組前面的句點 . 表示「目前套件內」,這是相對匯入的標準寫法,可以避免套件改名時要修改每一行 import。
用 pip 安裝第三方套件
pip 是 Python 內建的套件管理工具(從 Python 3.4 起預裝)。裝好的第三方套件會放到 site-packages 這個共享資料夾,所有專案都能呼叫。建議在每個專案裡都用 pip 明確指定版本,避免升級時造成不相容。當幾個專案共用全域 Python 環境時,常常會遇到「升級某個套件之後,另一個專案突然壞掉」的窘境,這正是後面 venv 章節要解決的問題。
pip install numpy
pip install pandas==2.2.3
pip install -r requirements.txt
pip list
pip show numpy
requirements.txt 是專案常用的套件清單檔,每行寫一個套件與版本,例如 numpy==2.1.3。把專案給別人時,只要附上這個檔案,對方就能用 pip install -r 一次裝齊。這是 Python 專案協作的標準做法,之後做資料科學專案時會大量用到。如果想要更精準地表達依賴關係(例如要求 numpy 至少 2.0 但不能超過 3.0),也可以在 requirements.txt 中寫成 numpy>=2.0,<3.0,pip 會自動挑選合適的版本。
用 venv 建立獨立環境
當你同時維護多個專案,可能會遇到 A 專案需要 Django 4、B 專案卻還在用 Django 3 的窘境。這時候用 venv 為每個專案建立獨立的虛擬環境,就能徹底隔離套件版本。venv 是 Python 3.3 起內建的標準工具,不需要額外安裝任何東西,是「官方推薦」的虛擬環境做法。
python -m venv .venv
# Windows 啟動
.venv\Scripts\activate
# macOS / Linux 啟動
source .venv/bin/activate
# 安裝套件後可以輸出清單
pip freeze > requirements.txt
# 離開環境
deactivate
啟動虛擬環境後,命令提示字元前面會出現 (.venv) 字樣,這時裝的套件只會活在這個資料夾裡。VS Code 與 PyCharm 都能自動偵測 .venv,建議每個新專案都從 venv 開始,避免污染全域的 Python。另外常見的命名習慣是把虛擬環境放在專案根目錄下的 .venv 或 venv 資料夾,並加入 .gitignore,這樣不同專案之間就不會互相干擾,部署到伺服器時也能用同一份 requirements.txt 還原出相同的環境。當你同時維護資料科學與網頁後端兩種專案時,venv 幾乎是必需品,否則套件版本衝突會讓你除錯除到天荒地老。
結語
今天把 Python 程式碼組織化的基礎都走過一輪:import 的各種寫法讓我們靈活引用別人的程式碼、自建模組與 __name__ 讓檔案可以被引用也可以單獨測試、套件結構把多個模組收納整齊、pip 與 requirements.txt 處理第三方套件、venv 則替每個專案建立乾淨的執行環境。這些工具看似瑣碎,卻是寫出可維護、可協作專案的關鍵。從今天起,建議每寫一個新專案就先建立 venv,把套件依賴關係從一開始就管好。
明天,我們會踏入物件導向的世界,從類別與物件開始學起。這是後續 PyTorch 神經網路模型的基礎觀念,因為到時候我們親手打造的 nn.Module 就是一個 class,懂物件導向才能看懂模型設計的邏輯。即使你目前只寫幾百行的小腳本,提前建立模組與套件的觀念,也能在日後接手大型專案時少走很多冤枉路。
留言
張貼留言