「Python 比 Java 簡單」這句話的一半是對的——Python 比 Java 簡單開始。
# Python:直接寫
def add(a, b):
return a + b// Java:需要型別宣告
public static int add(int a, int b) {
return a + b;
}Python 的版本更短,更直觀,初學者三分鐘就能跑起來。
但如果你的 codebase 長到 10 萬行,Python 的 add(a, b) 裡的 a 和 b 是什麼——整數?浮點數?複數?字串?你要讀函式實作或呼叫端才知道。
這個問題的規模,隨著 codebase 成長而放大。
靜態型別做了什麼
靜態型別系統:型別在編譯時(或在你寫 code 時,透過型別推斷)確定。如果你用一個 int 的地方放了 string,編譯器報錯,你的程式碼跑不起來。
function add(a: number, b: number): number {
return a + b;
}
add(1, "hello"); // 編譯錯誤,不需要執行就知道錯了靜態型別帶來的好處是:你在 IDE 裡就看到紅線,不需要跑到 production 才發現(編譯時錯誤 < 執行時錯誤);autocomplete、go-to-definition、refactoring 都依賴型別資訊,IDE 支援更強;函式簽名本身就是文件,說明「這個函式要什麼、給你什麼」。
動態型別的合理性
動態型別系統:型別在執行時確定。同一個變數可以在不同時間點是不同型別。
x = 5 # x 是 int
x = "hello" # x 現在是 str,沒問題
x = [1,2,3] # x 現在是 list,還是沒問題動態型別的優點在快速迭代場景:
- 寫 prototype 或腳本,不需要宣告型別就能跑
- Duck typing 讓程式碼有自然的彈性:函式接受「任何有
.read()方法的物件」,不管它是 File 還是 Socket 還是 StringIO
1990 年代的 web 開發,大多是小型單體應用,動態型別的快速迭代優勢是真實的。
型別推斷:不需要在靜態和動態之間選邊
現代靜態型別語言大量依賴型別推斷——你不需要到處寫型別宣告,編譯器自己推:
// Go:型別推斷
x := 5 // 推斷為 int
names := []string{"Alice", "Bob"} // 推斷為 []string
// 函式回傳型別推斷
func double(x int) int { // 這裡還是需要宣告參數型別
return x * 2
}// TypeScript:廣泛的推斷
const x = 5; // 推斷為 number
const arr = [1, 2, 3]; // 推斷為 number[]
const doubled = arr.map(n => n * 2); // 推斷為 number[]Rust 的型別推斷更強大,在某些情況下連函式參數都可以推斷(在 closure 裡)。
型別推斷讓靜態型別的寫法簡潔度接近動態型別,但保留了編譯時檢查的好處。
漸進式型別:最佳妥協?
動態語言加入可選的型別標注:
# Python type hints(可選,不影響 runtime)
def add(a: int, b: int) -> int:
return a + b
def process(items: list[str]) -> None:
for item in items:
print(item.upper())你可以在關鍵路徑加型別標注,用 mypy/pyright 做靜態分析,不加的地方繼續動態。
好處:不需要一次性重寫所有程式碼,可以逐步引入。
壞處:沒有型別標注的程式碼還是動態的,保護力不完整;型別標注是可選的,在 code review 沒有嚴格要求的情況下容易被省略。
TypeScript 對 JavaScript 也是這個思路——但 TypeScript 的覆蓋率通常比 Python type hints 更高,因為 TypeScript 的型別工具整合進 IDE 的體驗更好。
何時動態型別真的夠用
- 腳本和自動化工具:一次性的腳本,錯誤了重跑就好,靜態型別的收益不值得投入
- ML/資料分析:Python 在這個領域的生態系無法替代,動態型別是代價,NumPy/Pandas 的使用方式本來就是探索性的
- 小型單體服務:服務小、team 小、週期短,靜態型別的基礎建設(type stubs、CI 型別檢查)的投入可能大於收益
何時靜態型別是必要的
- 大型 codebase(> 50k 行):沒有型別資訊,你不知道任何一個函式的輸入期望是什麼
- 多人長期維護:六個月後讀別人寫的函式,型別簽名是最快的文件
- 公開 API:你的 SDK 如果沒有型別,呼叫方用起來效率極差
實際後端開發的策略
對於主要用動態語言的 team:
- 新寫的程式碼要有型別標注(Python type hints / TypeScript strict mode)
- 把
mypy/pyright/tsc --noEmit加進 CI,讓型別錯誤和測試失敗一樣會擋住 merge - 改動既有程式碼時順手補型別(童子軍原則)
對於靜態語言 team:
- 不要讓型別系統變成
any/interface{}/Object的堆砌——那是靜態型別的形式,動態型別的本質 - 善用型別系統表達業務規則(
OrderIDvsUserID都是int,但型別系統能防止你把 order ID 傳進 user 查詢)