本文 AI 產出,尚未審核

Python 課程 – 函式(Functions)

主題:型別提示(type hints)


簡介

在 Python 中,函式的參數與回傳值本質上是「動態」的,執行時才決定實際型別。這種彈性讓語言非常友好,但在大型專案或多人協作時,缺乏明確的型別資訊往往會導致 程式錯誤難以追蹤、IDE 的自動補完失效,甚至降低程式碼可讀性。

自 Python 3.5 起加入的 型別提示(type hints),提供了一套靜態型別註記的語法。它不會改變程式的執行行為,卻能讓開發者在編寫階段就得到類似編譯語言的好處:提前發現錯誤、提升 IDE 智慧提示、生成更清晰的文件。對於從初學者晉升為中級開發者的你,掌握型別提示是提升程式品質的重要一步。


核心概念

1️⃣ 基本語法:->:

  • 參數型別:在參數名稱後加上冒號 :,接著寫型別。
  • 回傳型別:在函式簽名最後加上 ->,再寫回傳值的型別。
def add(a: int, b: int) -> int:
    """將兩個整數相加,回傳結果為整數。"""
    return a + b

重點:即使加上型別提示,Python 仍會接受任何型別的實參。若想在執行時強制檢查,需要額外使用工具(如 typeguard)。


2️⃣ 常用型別與 typing 模組

內建型別(intstrfloatbool)之外,typing 提供了許多 抽象型別,讓我們能描述更複雜的結構。

型別 說明
List[T] 同步列表,元素型別為 T
Tuple[T1, T2, ...] 固定長度的元組
Dict[K, V] 字典,鍵型別 K、值型別 V
Optional[T] TNone(等同於 Union[T, None]
Union[A, B] 可能是 AB
Callable[[A, B], R] 接受 A, B 參數、回傳 R 的函式
Any 任意型別,等同於不加提示
from typing import List, Tuple, Dict, Optional, Union, Callable, Any

def process_data(data: List[int]) -> Tuple[int, int]:
    """回傳 (最小值, 最大值)。"""
    return min(data), max(data)

def greet(name: Optional[str] = None) -> str:
    """若未提供 name,回傳預設問候。"""
    return f"Hello, {name or 'Guest'}!"

def apply(func: Callable[[int, int], int], x: int, y: int) -> int:
    """將傳入的二元函式套用於 x, y。"""
    return func(x, y)

def echo(value: Any) -> Any:
    """回傳相同的值,型別不做限制。"""
    return value

3️⃣ 泛型(Generics)

當函式需要接受 任意型別的容器,卻仍想保留元素型別資訊,可使用 TypeVar 定義 泛型變數

from typing import TypeVar, Sequence

T = TypeVar('T')          # 任意型別

def first_item(seq: Sequence[T]) -> T:
    """回傳序列的第一個元素,保持原始型別。"""
    return seq[0]

# 使用範例
num = first_item([1, 2, 3])          # 推斷為 int
word = first_item(("a", "b", "c"))   # 推斷為 str

技巧:在大型程式庫中,透過泛型可以讓 API 的型別資訊更精準,IDE 也能正確推斷返回值型別。


4️⃣ 延遲評估的字串型別(PEP 563)

Python 3.7 之後,可在檔案最上方加入 from __future__ import annotations,讓所有型別提示在執行時以 字串 形式保存,避免循環引用或前置宣告的問題。

from __future__ import annotations
from typing import List

class Node:
    def __init__(self, value: int, children: List[Node] | None = None):
        self.value = value
        self.children = children or []

這樣寫的好處是 型別提示不會在模組載入時立即求值,提升效能並簡化相互依賴的類別定義。


5️⃣ 靜態檢查工具

型別提示本身不會執行檢查,必須結合 靜態分析工具(如 mypypyrightpylint)才能在開發階段發現不匹配的情況。

# 安裝 mypy
pip install mypy

# 檢查 my_module.py
mypy my_module.py

實務建議:在 CI/CD 流程中加入 mypy --strict,確保所有 PR 都符合型別規範。


常見陷阱與最佳實踐

陷阱 說明 解決方式
忘記匯入 typing 直接使用 ListDict 等會報 NameError 在檔案開頭 from typing import List, Dict, ...
使用可變預設值 def f(arg: List[int] = []): 會共用同一個列表。 改為 `def f(arg: List[int]
過度使用 Any 失去型別提示的意義,錯誤不易捕捉。 盡量使用具體型別或 Protocol 取代 Any
忽略 Optional 把可能為 None 的參數寫成 str,導致 NoneType 錯誤。 明確寫 Optional[str],或在程式內檢查 if value is None:
循環引用 兩個類別相互引用時,型別提示會導致 NameError 使用 前置字串(PEP 563)或 typing.TYPE_CHECKING 條件匯入。

最佳實踐

  1. 從小範圍開始:先為公開 API(函式、類別方法)加上型別提示,再逐步擴展到內部實作。
  2. 遵守 PEP 484:遵循官方型別提示規範,確保程式碼在不同工具間的相容性。
  3. 使用 Literal:對於只能接受固定值的參數,用 Literal 提高可讀性與檢查精度。
from typing import Literal

def set_mode(mode: Literal["r", "w", "a"]) -> None:
    """只能接受 'r', 'w', 'a' 三種模式。"""
    print(f"Mode set to {mode}")
  1. 加入文件字串:在函式說明中同時寫下型別說明,讓使用者即使不看型別提示也能了解參數需求。
  2. 在 CI 中加入型別檢查:將 mypy --strictpyright 作為測試的一部份,防止型別回退。

實際應用場景

1️⃣ 大型 Web API 專案

在 FastAPI、Django REST Framework 等框架中,型別提示直接影響自動產生的文件(OpenAPI、Swagger)。例如:

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    price: float
    tags: list[str] = []

@app.post("/items/")
def create_item(item: Item) -> Item:
    """回傳建立後的商品資訊。"""
    return item

FastAPI 會根據 Item 的型別自動產生 JSON Schema,讓前端開發者即時取得正確的欄位資訊。

2️⃣ 數據分析與機器學習管線

在 pandas、NumPy、scikit-learn 等庫的組合使用中,型別提示能協助辨識 DataFrameSeries 的欄位型別,減少因欄位名稱拼寫錯誤導致的 KeyError

import pandas as pd
from typing import Literal

def filter_by_category(df: pd.DataFrame, column: str, category: Literal["A", "B", "C"]) -> pd.DataFrame:
    """依照特定類別過濾 DataFrame。"""
    return df[df[column] == category]

3️⃣ 內部工具與自動化腳本

即使是小型腳本,加入型別提示也能提升維護性。當同事接手時,函式簽名即是使用說明,減少溝通成本。

def copy_file(src: str, dst: str, overwrite: bool = False) -> None:
    """將檔案從 src 複製到 dst,若 overwrite 為 True 則允許覆寫。"""
    import shutil, os
    if not overwrite and os.path.exists(dst):
        raise FileExistsError(f"{dst} already exists")
    shutil.copy2(src, dst)

總結

型別提示是 Python 靜態型別檢查的核心工具,它不會改變程式的執行結果,但能在開發階段提供:

  • 更好的可讀性 – 函式簽名即是使用說明。
  • 提前捕捉錯誤 – 配合 mypypyright 等工具,減少執行時例外。
  • IDE 智慧提示 – 自動補完與跳轉更精準。
  • 文件自動生成 – 如 FastAPI、Pydantic 可直接產生 API 規格。

對於從 初學者中級開發者,建議逐步將型別提示納入日常開發流程:先從公共函式開始,使用 typing 中的基本型別,然後慢慢引入泛型、LiteralProtocol 等進階概念,最後在 CI 中加入嚴格的型別檢查。如此一來,你的程式碼將更易維護、更具可擴充性,也能在團隊合作時展現專業水準。祝你在 Python 的型別世界裡寫出更安全、更清晰的程式!