第三方模組怎麼選?從需求、授權到安全更新
會安裝套件只是起點;真正的能力,是知道為何選、如何驗證、何時更新與怎麼退回。
完成本篇後,你能區分標準函式庫、第三方發行套件與匯入模組;從功能、維護、授權、安全及部署條件評估套件;使用venv隔離環境;建立可重現的依賴清單;進行基本弱點稽核,並為USR場域制定安全更新與回復流程。
一、先問「需要什麼」,再問「裝哪一個」
水井村智慧生活專案可能需要網頁請求、影像處理、資料分析、資料庫、斷詞與硬體通訊。每多一個第三方套件,就多一份功能,也多一份版本、授權、弱點與維護責任。
| USR需求 | 先檢查 | 可能使用 |
|---|---|---|
| 呼叫YouBike或場域API | 標準urllib是否足夠?是否需Session、重試? | requests、httpx |
| 處理水井三寶圖卡 | 只需縮圖,還是要進階影像運算? | Pillow、OpenCV |
| 整理感測資料 | 內建csv能否完成?資料量多大? | pandas、NumPy |
| 存取MySQL | 直接SQL還是需要ORM? | PyMySQL、SQLAlchemy |
| 中文文字處理 | 語言、詞典與授權是否合適? | 依任務比較斷詞工具 |
二、Package、Distribution與Module不是同一件事
日常口語都稱「套件」,但安裝名稱與匯入名稱可能不同。pip安裝的是發行套件(distribution package),Python程式匯入的是模組或import package。
import importlib.metadata
distribution_name = "Pillow"
print(importlib.metadata.version(distribution_name))
# 安裝名稱是Pillow,匯入名稱卻是PIL
from PIL import Image
print(Image.__name__)因此不能只用 import名稱 == pip名稱 猜測。應以套件官方文件及PyPI專案頁為準,並小心拼字相近的惡意套件。
三、七項評估表:先評分再安裝
| 面向 | 檢查問題 | 證據 |
|---|---|---|
| 需求適配 | 能否用最少功能完成任務? | 最小原型與測試 |
| 維護狀態 | 是否仍支援所用Python版本? | 官方文件、發行紀錄 |
| 授權 | 教學、商用、再散布是否允許? | LICENSE與專案metadata |
| 安全 | 是否有未修補已知弱點? | 安全公告與依賴稽核 |
| 可部署性 | 平台、CPU、記憶體與系統函式庫是否相容? | 乾淨環境安裝測試 |
| 資料治理 | 資料是否會外傳?預設遙測為何? | 隱私政策與網路測試 |
| 可替換性 | 若停止維護,能否退出? | 包裝介面與回復計畫 |
四、用Python建立簡易評估紀錄
from dataclasses import dataclass
@dataclass(frozen=True)
class PackageReview:
name: str
need_fit: int
maintenance: int
license_fit: int
security: int
deployability: int
evidence: str
@property
def total(self):
scores = (
self.need_fit,
self.maintenance,
self.license_fit,
self.security,
self.deployability,
)
if any(score not in range(1, 6) for score in scores):
raise ValueError("各項評分必須是1到5")
return sum(scores)
review = PackageReview(
name="示範套件",
need_fit=5,
maintenance=4,
license_fit=4,
security=4,
deployability=5,
evidence="官方文件、最小原型、授權檔與測試報告",
)
print(review.total)分數不能取代判斷;若授權不合、存在無法接受的弱點或會把敏感資料送出,即使總分高也不應採用。
五、每個專案都建立自己的虛擬環境
PyPA建議使用虛擬環境管理第三方套件,避免不同專案互相干擾。Windows與macOS/Linux啟用方式不同:
$ py -m venv .venv
$ .venv\Scripts\activate
$ python -m pip install --upgrade pip
# macOS或Linux
$ python3 -m venv .venv
$ source .venv/bin/activate
$ python -m pip install --upgrade pip在專案根目錄建立 .venv,並把它加入.gitignore。不要把整個虛擬環境提交到版本控制;應提交依賴宣告,讓環境可以重建。
六、確認目前真的在虛擬環境
import sys
from pathlib import Path
in_virtual_environment = sys.prefix != sys.base_prefix
print("Python:", Path(sys.executable))
print("虛擬環境:", in_virtual_environment)啟用只是方便shell找到正確Python;關鍵是執行的直譯器路徑。Thonny、VS Code、排程或網站服務也要指定同一環境。
七、用python -m pip避免裝錯地方
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-m", "pip", "--version"],
check=True,
capture_output=True,
text=True,
)
print(result.stdout.strip())python -m pip可明確使用目前Python所屬的pip,減少電腦同時安裝多個Python時的混亂。
八、不要無條件安裝「最新版」
開發初期可指定一段相容範圍,正式部署則需要經測試的解析結果。以下只是格式示範,不宣稱這些版本永遠合適:
requests>=2.0,<3.0
Pillow>=10.0,<13.0
SQLAlchemy>=2.0,<3.0
PyMySQL>=1.1,<2.0版本範圍要依支援政策與測試調整。對教室教材,可以提供一份已驗證版本;對正式系統,升級應經過測試、稽核、備份與回復流程。
九、requirements.txt與pip freeze
$ python -m pip install -r requirements.txt
$ python -m pip freeze > requirements-lock.txt
$ python -m pip checkpip freeze記錄目前環境所有已安裝版本,適合製作環境快照,但可能把未使用的套件也收入。較成熟的專案會分開維護「直接需求」與「解析後鎖定結果」。
十、用metadata查看版本、授權與依賴
from importlib.metadata import metadata, requires, version
def package_summary(distribution_name):
info = metadata(distribution_name)
return {
"name": info.get("Name"),
"version": version(distribution_name),
"license_expression": info.get("License-Expression"),
"license": info.get("License"),
"home_page": info.get("Home-page"),
"dependencies": requires(distribution_name) or [],
}
print(package_summary("pip"))Metadata可以協助盤點,但授權欄位可能缺漏或表達方式不同。真正採用前仍要讀專案LICENSE、例外條款與所包含資料或模型的授權。
十一、產生最小軟體物料清單
import importlib.metadata
import json
from datetime import datetime, timezone
packages = sorted(
(
{
"name": item.metadata.get("Name", item.name),
"version": item.version,
}
for item in importlib.metadata.distributions()
),
key=lambda item: item["name"].lower(),
)
inventory = {
"created_at": datetime.now(timezone.utc).isoformat(),
"python": __import__("sys").version,
"packages": packages,
}
print(json.dumps(inventory, ensure_ascii=False, indent=2))這是教學用盤點,不是完整標準SBOM。至少要知道部署了哪些元件與版本,發生安全公告時才能判斷是否受影響。
十二、檢查已知弱點,但不要誤解結果
PyPA的pip-audit可掃描Python環境中套件的已知弱點。它不是原始碼掃描器,也不能保證發現所有風險;掃描前仍要把套件視為會被解析與處理的外部輸入。
$ python -m pip install pip-audit
$ python -m pip_audit
$ python -m pip_audit -r requirements-lock.txt十三、雜湊可以強化可重現安裝
pip支援hash-checking mode,requirements中的套件可附上允許的檔案雜湊,再用 --require-hashes 驗證。雜湊能確認下載檔案符合鎖定內容,但不能證明套件本身沒有惡意程式或設計缺陷。
$ python -m pip install \
--require-hashes \
-r requirements-hashed.txt十四、授權不是只有「免費」與「付費」
| 檢查項目 | 教學與USR要問的問題 |
|---|---|
| 程式碼授權 | 可否修改、散布、商用?需否保留聲明? |
| 資料/模型授權 | 圖片、詞典、模型權重是否另有條款? |
| Copyleft義務 | 與自有程式結合或部署服務時有何義務? |
| 專利與商標 | 程式碼授權是否同時處理專利或名稱使用? |
| 隱私與服務條款 | 雲端API是否保存、再利用或跨境傳輸資料? |
遇到正式商用、再散布或複雜授權時,應由具資格的法務或授權專業人員確認,不能只靠AI摘要下結論。
十五、先做煙霧測試,再讓套件進場域
from importlib import import_module
from importlib.metadata import version
EXPECTED = {
"requests": "requests",
"Pillow": "PIL",
"PyMySQL": "pymysql",
"SQLAlchemy": "sqlalchemy",
}
def smoke_test(distribution_name):
module_name = EXPECTED[distribution_name]
module = import_module(module_name)
return {
"distribution": distribution_name,
"version": version(distribution_name),
"imported_as": module.__name__,
}
for name in EXPECTED:
try:
print(smoke_test(name))
except Exception as error:
print(name, "FAIL", type(error).__name__)煙霧測試只能確認基本匯入。還要測USR實際流程,例如圖片能否正確縮圖、MySQL能否回滾、Raspberry Pi能否安裝、斷線後資料能否補傳。
十六、用介面包住第三方套件
不要讓整個專案到處直接依賴某套件。把外部功能包在小型介面後,較容易測試、替換與集中處理錯誤:
class ImageThumbnailer:
def __init__(self, image_module):
self.image_module = image_module
def create(self, source, destination, size=(800, 800)):
with self.image_module.open(source) as image:
image.thumbnail(size)
image.save(destination)
from PIL import Image
thumbnailer = ImageThumbnailer(Image)這也讓測試時能傳入替身物件,不必每次真的處理大型圖片或呼叫外部服務。
十七、安全更新的六步驟
- 盤點:確認直接與間接依賴、Python與作業系統版本。
- 閱讀:查看官方發行說明、安全公告與破壞性變更。
- 備份:保存可回復的程式版本、設定與資料庫備份。
- 測試:在乾淨環境重建,執行單元、整合與場域流程測試。
- 分段部署:先測試站或少量設備,觀察Log、效能與錯誤率。
- 記錄與回復:留下版本、日期、負責人、驗證結果及退版條件。
from dataclasses import dataclass
from datetime import date
@dataclass
class UpgradeRecord:
package: str
old_version: str
new_version: str
tested_on: date
tests_passed: bool
rollback_tag: str
reviewer: str
record = UpgradeRecord(
package="示範套件",
old_version="1.0",
new_version="1.1",
tested_on=date.today(),
tests_passed=True,
rollback_tag="before-demo-upgrade",
reviewer="課程小組",
)
print(record)十八、USR場域的特別風險
| 場域 | 常見限制 | 選套件時的因應 |
|---|---|---|
| 養殖池邊緣設備 | 網路不穩、ARM平台、儲存有限 | 先測離線、wheel支援與資源用量 |
| 長者與社區資料 | 語音、照片可能含個資 | 優先本機處理,確認外傳與保存規則 |
| 學生共同開發 | 環境不一致、帳密誤傳 | 虛擬環境、鎖定檔、Secrets與程式審查 |
| 長期USR維運 | 學生畢業、套件停更 | 文件化、介面隔離、替代方案與交接 |
十九、課堂挑戰
二十、用AI當評估助教,但證據要回到官方來源
請擔任Python第三方套件評估助教。
我的任務、Python版本、作業系統與部署硬體如下:〔請填寫〕。
請先問清楚必要功能,再比較不超過3個候選套件。
評估需求適配、官方維護、Python支援、授權、安全公告、
間接依賴、安裝大小、ARM與離線部署、資料是否外傳。
每項必須指向官方文件、PyPI metadata或專案LICENSE,
不以下載量或AI印象直接下結論。
最後提出最小驗證、更新與退版計畫。
沒有留言:
張貼留言