本文 AI 產出,尚未審核

Python 課程 – 型別提示與靜態分析

主題:pyright / pylance


簡介

在 Python 生態系統裡,型別提示(type hints) 已經從可選的語法糖,逐漸演變成大型專案維護與協作的基礎工具。搭配靜態分析器,開發者可以在寫程式的同時即時捕捉潛在錯誤、提升 IDE 補完品質,甚至在 CI/CD 流程中加入型別檢查,降低部署風險。

pyright 是 Microsoft 開源的高速型別檢查器,而 pylance 則是基於 pyright 的 VS Code 智慧感知(IntelliSense)擴充套件。兩者共同提供 即時、準確且效能佳 的型別分析體驗,對於想在 Python 中導入型別安全的開發者來說,是不可或缺的工具。

本篇文章將從概念說明、實作範例、常見陷阱與最佳實踐,帶你完整掌握 pyright / pylance 的使用方式,並分享在真實專案中的應用場景。


核心概念

1. 為什麼需要靜態型別分析?

需求 傳統動態檢查 靜態型別分析
早期發現錯誤 只能在執行時才知道 編寫時即顯示錯誤
IDE 補完與文件提示 只能靠 docstring 完整的型別資訊
大型團隊協作 隱式約定易產生誤解 明確合約 (contract)
CI/CD 檢查 需要額外測試 直接在 lint 步驟完成

pyright 透過 AST(抽象語法樹)型別推斷演算法,在毫秒級別完成分析,遠快於傳統的 mypy。pylance 把這個引擎嵌入 VS Code,讓開發者在編輯器裡即時看到錯誤、建議與型別資訊。

2. 安裝與基本設定

# 1. 全域安裝 pyright(可在任何編輯器使用)
npm install -g pyright   # 需要 Node.js 環境

# 2. VS Code 安裝 pylance(Marketplace 直接安裝)
# 或者使用指令列安裝
code --install-extension ms-python.vscode-pylance

在專案根目錄建立 pyrightconfig.json,讓 pyright 知道要檢查的範圍與型別嚴格度:

{
  "typeCheckingMode": "strict",
  "exclude": ["**/tests/**", "dist/"],
  "reportMissingImports": true,
  "reportUnusedVariable": true
}
  • typeCheckingMode 可設定為 offbasicstrictstrict 會啟用所有嚴格檢查,最適合 CI/CD 使用。
  • exclude 用來排除不需要檢查的目錄,避免噪音。

3. 基本型別提示語法

def greet(name: str) -> str:
    """回傳問候語"""
    return f"Hello, {name}!"
  • name: str 表示 name 必須是字串。
  • -> str 表示函式回傳值的型別。

pyright 會在 呼叫 greet(123) 時即時報錯:

Argument of type "int" is not assignable to parameter "name" of type "str"

4. 常見型別工具

工具 用途 範例
List[T] / Dict[K, V] 泛型容器 def total(values: List[int]) -> int:
Optional[T] 可能為 None def find(key: str) -> Optional[int]:
Union[A, B] 多種可能型別 def parse(data: Union[str, bytes]) -> str:
TypedDict 結構化字典 class User(TypedDict): name: str; age: int
Protocol 靜態介面 class SupportsClose(Protocol): def close(self) -> None: ...

5. pyright 的型別推斷與報告

範例 1:自動推斷容器內型別

from typing import List

def first_item(items: List[int]) -> int:
    # pyright 會推斷 items[0] 為 int
    return items[0]

# 呼叫時若傳入 List[str],會得到錯誤訊息
first_item(["a", "b"])
# ──> Argument of type "List[str]" is not assignable to parameter "items" of type "List[int]"

範例 2:使用 TypedDict 定義 API 回傳結構

from typing import TypedDict, List

class Book(TypedDict):
    title: str
    author: str
    pages: int

def get_book() -> Book:
    # 正確回傳結構
    return {"title": "Python 入門", "author": "Alice", "pages": 250}

def print_book(b: Book) -> None:
    print(f"{b['title']} by {b['author']} ({b['pages']} 頁)")

print_book(get_book())

如果忘記 pages 欄位,pyright 會立刻提示缺少必填鍵:

Property "pages" is missing in type "{ title: str; author: str; }"

範例 3:Protocol 讓 duck-typing 受靜態檢查保護

from typing import Protocol

class SupportsWrite(Protocol):
    def write(self, data: str) -> int: ...

def log(msg: str, dest: SupportsWrite) -> None:
    dest.write(msg + "\n")

class FileWriter:
    def __init__(self, path: str) -> None:
        self.file = open(path, "a", encoding="utf-8")
    def write(self, data: str) -> int:
        return self.file.write(data)

log("開始執行", FileWriter("log.txt"))

即使 FileWriter 並未繼承 SupportsWrite,只要符合方法簽名,pyright 就會認可,避免因忘記實作 write 而產生的 runtime error。

範例 4:Literal 限制可接受的常數值

from typing import Literal

def set_mode(mode: Literal["debug", "release"]) -> None:
    print(f"Mode set to {mode}")

set_mode("debug")    # OK
set_mode("test")     # pyright: Argument of type '"test"' is not assignable to parameter "mode"

範例 5:在 pyrightconfig.json 中自訂型別檔案路徑

假設專案有自訂的第三方 stub (.pyi) 檔案:

{
  "typeCheckingMode": "strict",
  "stubPath": "./stubs"
}

這樣 pyright 會自動載入 ./stubs 內的型別資訊,讓即使沒有原始碼的套件也能得到完整檢查。


常見陷阱與最佳實踐

陷阱 說明 解法 / Best Practice
忽略 type: ignore 隨意加 # type: ignore 會掩蓋真正的問題。 只在確定無法解決的第三方 stub 時使用,並加上說明。
過度使用 Any Any 會讓靜態檢查失效,等同於不寫型別。 盡量使用具體型別或 Protocol,必要時使用 cast()
未設定 strict 預設 basic 只檢查明顯錯誤,漏掉許多潛在問題。 在 CI/CD 中使用 typeCheckingMode: "strict",本機開發則可視需求調整。
忘記更新 stub 第三方套件更新後,型別定義可能不相容。 使用 pip install -U 後,同步更新 pyrightconfig.json 中的 stubPath 或重新安裝 types- 套件。
未啟用 reportUnusedVariable 未使用的變數會成為雜訊,降低可讀性。 pyrightconfig.json 開啟 reportUnusedVariable,配合 # noqa: F401 只在必要時抑制。
使用相對匯入時路徑錯誤 pyright 可能找不到模組,報 Import "x" could not be resolved. pyrightconfig.json 加入 "venvPath": "./.venv""extraPaths": ["src"]

最佳實踐總結

  1. strict 開始:即使在開發階段,嚴格模式可以提前發現問題。
  2. 把型別檢查納入 CI:在 github actionsGitLab CI 中執行 pyright,確保每次 PR 都通過。
  3. 分層使用 # type: ignore:只在不可避免的情況下使用,並加上註解說明原因。
  4. 善用 TypedDictProtocol:取代過度使用 dict 或裸 class,提升可讀性與檢查精度。
  5. 保持 stub 更新:對於常用的第三方套件(如 requestspandas),安裝對應的 types- 套件或自行維護 stub。

實際應用場景

1. 大型微服務系統的 API 合約檢查

在微服務架構中,各服務之間透過 JSON/REST 互相呼叫。利用 TypedDict + pyright,開發者可以在 client SDK 中定義回傳結構,若服務端回傳欄位遺失或型別變更,SDK 使用者會在編譯時即收到警告,避免因不相容的 API 造成的 runtime error。

2. 數據科學專案的資料管道

資料清洗、特徵工程階段常常以 pandas.DataFrame 為主,型別不明確容易導致錯誤。結合 pandas-stubspip install pandas-stubs)與 pyright,可在 DataFrame 的列操作上得到型別推斷,例如:

import pandas as pd
from pandas import DataFrame

def filter_age(df: DataFrame) -> DataFrame:
    # pyright 會推斷 df["age"] 為 Series[int]
    return df[df["age"] > 18]

若不小心把 age 欄位寫成字串,pyright 會直接報錯。

3. CI/CD 中的型別安全門檻

在 GitHub Actions 工作流程中加入以下步驟,即可在 PR 合併前保證型別正確:

name: Python Type Check
on: [push, pull_request]

jobs:
  type-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: "3.11"
      - name: Install dependencies
        run: |
          python -m pip install -r requirements.txt
          npm install -g pyright
      - name: Run pyright
        run: pyright --project .

只要 pyright 回傳非零狀態碼,CI 會失敗,確保所有型別問題在合併前被修正。

4. VS Code 中的即時協助

開發者在編寫函式時,pylance 會自動顯示 參數型別回傳型別,以及 可能的錯誤。例如:

def calculate(a: int, b: int) -> float:
    return a / b

當你在另一檔案呼叫 calculate("5", 2),pylance 會在編輯器底部顯示紅色波浪線,並在 hover 時提示:

Argument of type "str" is not assignable to parameter "a" of type "int".

這種即時回饋大幅降低除錯時間,尤其在多人協作的代碼基礎上更顯價值。


總結

  • pyright 是一個高速、可自訂的型別檢查器,適合在任何編輯器或 CI 環境中使用。
  • pylance 把 pyright 的引擎嵌入 VS Code,提供即時錯誤提示、智能補完與文件說明,提升開發效率。
  • 透過 type hintsTypedDictProtocolLiteral 等工具,開發者可以在 Python 中建立 明確的型別合約,減少 runtime 錯誤。
  • 嚴格模式(strict)CI 整合適當的 # type: ignore 使用,是確保型別安全的關鍵實踐。
  • 在微服務、資料科學、CI/CD 等實務場景中,pyright/pylance 已被證明能提升程式碼品質、降低維護成本。

結語:型別提示不再是「額外負擔」,而是 提升程式碼可讀性、可維護性與安全性 的必備工具。只要配合 pyright/pylance,從現在開始把「寫對」變成「寫好」的日常習慣吧!