Python 3.5 引入了型別提示(Type Hints),讓你可以為變數和函式添加型別註解。型別提示不會影響執行,但能大幅提升程式碼的可讀性和 IDE 的智慧提示功能。

為什麼需要型別提示?

沒有型別提示的程式碼

1def process(data):
2    return data.strip()
3
4result = process(input_value)  # data 是什麼型別?strip() 能用嗎?

有型別提示的程式碼

1def process(data: str) -> str:
2    return data.strip()
3
4result = process(input_value)  # 清楚知道需要字串,返回字串

基本語法

變數型別註解

1# 基本型別
2name: str = "Python"
3count: int = 42
4ratio: float = 3.14
5is_valid: bool = True
6
7# 可以不賦值(用於宣告)
8message: str  # 稍後賦值

函式型別註解

1def greet(name: str) -> str:
2    return f"Hello, {name}!"
3
4def add(a: int, b: int) -> int:
5    return a + b
6
7def print_message(msg: str) -> None:
8    print(msg)  # 沒有返回值用 None

實際範例:Hook 系統

來自 .claude/lib/git_utils.py 的範例:

 1def run_git_command(
 2    args: list[str],
 3    cwd: Optional[str] = None,
 4    timeout: int = 10
 5) -> tuple[bool, str]:
 6    """
 7    執行 git 命令並返回結果
 8
 9    Args:
10        args: git 命令參數列表(不含 'git')
11        cwd: 執行目錄,預設為當前目錄
12        timeout: 命令超時時間(秒)
13
14    Returns:
15        tuple[bool, str]: (是否成功, 輸出內容或錯誤訊息)
16    """
17    try:
18        result = subprocess.run(
19            ["git"] + args,
20            cwd=cwd,
21            capture_output=True,
22            text=True,
23            timeout=timeout
24        )
25        if result.returncode == 0:
26            return True, result.stdout.strip()
27        else:
28            return False, result.stderr.strip()
29    except subprocess.TimeoutExpired:
30        return False, f"Command timed out after {timeout}s"

分析這個函式的型別提示:

參數型別說明
argslist[str]字串列表
cwdOptional[str]可選字串,可以是 None
timeoutint整數,有預設值
返回值tuple[bool, str]布林和字串組成的元組

容器型別

列表(List)

1from typing import List  # Python 3.9 前需要
2
3# Python 3.9+
4def process_names(names: list[str]) -> list[str]:
5    return [name.upper() for name in names]
6
7# Python 3.8 及之前
8def process_names(names: List[str]) -> List[str]:
9    return [name.upper() for name in names]

字典(Dict)

1from typing import Dict
2
3# Python 3.9+
4def get_config() -> dict[str, int]:
5    return {"timeout": 10, "retries": 3}
6
7# Python 3.8 及之前
8def get_config() -> Dict[str, int]:
9    return {"timeout": 10, "retries": 3}

集合(Set)

1# Python 3.9+
2def get_unique_items(items: list[str]) -> set[str]:
3    return set(items)

元組(Tuple)

1# 固定長度和型別
2def get_position() -> tuple[int, int]:
3    return (10, 20)
4
5# 可變長度(同質)
6def get_values() -> tuple[int, ...]:
7    return (1, 2, 3, 4, 5)

實際應用:Hook 輸出建立

來自 .claude/lib/hook_io.py

 1def create_pretooluse_output(
 2    decision: str,
 3    reason: str,
 4    user_prompt: Optional[str] = None,
 5    system_message: Optional[str] = None,
 6    suppress_output: bool = False
 7) -> dict:
 8    """
 9    建立 PreToolUse Hook 輸出格式
10
11    Args:
12        decision: 決策結果 ("allow" | "deny" | "ask")
13        reason: 決策原因說明
14        user_prompt: 詢問用戶的訊息
15        system_message: 系統訊息
16        suppress_output: 是否抑制輸出
17    """
18    output: dict[str, Any] = {
19        "hookSpecificOutput": {
20            "hookEventName": "PreToolUse",
21            "permissionDecision": decision,
22            "permissionDecisionReason": reason
23        }
24    }
25
26    if user_prompt:
27        output["hookSpecificOutput"]["userPrompt"] = user_prompt
28
29    if system_message:
30        output["systemMessage"] = system_message
31
32    if suppress_output:
33        output["suppressOutput"] = True
34
35    return output

型別別名

為複雜型別建立別名提升可讀性:

 1from typing import Dict, List, Tuple
 2
 3# 定義型別別名
 4ValidationResult = Tuple[bool, str]
 5ConfigDict = Dict[str, Any]
 6NameList = List[str]
 7
 8# 使用別名
 9def validate_input(data: str) -> ValidationResult:
10    if data:
11        return True, "Valid"
12    return False, "Empty input"
13
14def load_config() -> ConfigDict:
15    return {"key": "value"}

型別檢查工具

型別提示本身不會在執行時檢查,但可以用工具進行靜態檢查:

mypy

1# 安裝
2pip install mypy
3
4# 檢查檔案
5mypy my_script.py
6
7# 檢查目錄
8mypy .claude/lib/

IDE 整合

現代 IDE(VS Code、PyCharm)會自動利用型別提示:

  • 自動完成更準確
  • 型別錯誤即時提示
  • 重構更安全

最佳實踐

1. 為公開 API 添加型別提示

1# 公開函式必須有型別提示
2def get_current_branch() -> Optional[str]:
3    """獲取當前分支名稱"""
4    ...
5
6# 內部輔助函式可以省略
7def _parse_output(text):
8    ...

2. 使用有意義的型別別名

1# 好:清楚表達意圖
2BranchName = str
3ValidationResult = Tuple[bool, str]
4
5def validate_branch(name: BranchName) -> ValidationResult:
6    ...
7
8# 不好:型別別名沒有增加資訊
9MyStr = str  # 這有什麼意義?

3. 逐步添加型別提示

不需要一次為所有程式碼添加型別提示,可以從以下開始:

  • 公開 API
  • 複雜函式
  • 經常被呼叫的函式

思考題

  1. list[str]List[str] 有什麼區別?什麼時候用哪個?
  2. 為什麼 run_git_command 返回 tuple[bool, str] 而不是自定義類別?
  3. 型別提示會影響程式執行速度嗎?

實作練習

為以下函式添加適當的型別提示:

 1def parse_config(file_path):
 2    with open(file_path) as f:
 3        return json.load(f)
 4
 5def filter_valid_items(items):
 6    return [item for item in items if item.get("valid")]
 7
 8def merge_dicts(dict1, dict2):
 9    result = dict1.copy()
10    result.update(dict2)
11    return result

下一章:Optional、Union、泛型