2.2 Optional、Union、泛型
2.2 Optional、Union、泛型
當函式的參數或返回值可能是多種型別時,我們需要使用 Optional、Union 或泛型來表達。這些工具讓型別提示更加精確。
Optional
Optional[T] 表示值可能是 T 型別,也可能是 None。
基本用法
1from typing import Optional
2
3def get_current_branch() -> Optional[str]:
4 """
5 獲取當前分支名稱
6
7 Returns:
8 str | None: 分支名稱,如果無法獲取則返回 None
9 """
10 success, output = run_git_command(["branch", "--show-current"])
11 return output if success and output else NoneOptional 等同於 Union[T, None]
1from typing import Optional, Union
2
3# 這兩種寫法等價
4def find_user(id: int) -> Optional[User]:
5 ...
6
7def find_user(id: int) -> Union[User, None]:
8 ...
9
10# Python 3.10+ 可以使用 | 語法
11def find_user(id: int) -> User | None:
12 ...實際範例:配置載入
來自 .claude/lib/config_loader.py:
1from typing import Optional
2
3# 模組級快取變數
4_agents_config_cache: Optional[dict] = None
5_quality_rules_cache: Optional[dict] = None
6
7def load_agents_config() -> dict:
8 """載入代理人配置"""
9 global _agents_config_cache
10
11 # 快取為 None 表示尚未載入
12 if _agents_config_cache is None:
13 try:
14 _agents_config_cache = load_config("agents")
15 except FileNotFoundError:
16 _agents_config_cache = _get_default_agents_config()
17
18 return _agents_config_cacheUnion
Union[A, B, C] 表示值可能是 A、B 或 C 型別。
基本用法
1from typing import Union
2
3def process_input(data: Union[str, bytes]) -> str:
4 """處理字串或位元組輸入"""
5 if isinstance(data, bytes):
6 return data.decode("utf-8")
7 return data
8
9# Python 3.10+ 語法
10def process_input(data: str | bytes) -> str:
11 ...常見應用
1from typing import Union, List
2
3# 接受單一值或列表
4def ensure_list(value: Union[str, List[str]]) -> List[str]:
5 if isinstance(value, str):
6 return [value]
7 return value
8
9# 返回不同型別
10def parse_value(text: str) -> Union[int, float, str]:
11 try:
12 return int(text)
13 except ValueError:
14 try:
15 return float(text)
16 except ValueError:
17 return text泛型(Generic)
泛型讓你建立可以與多種型別一起使用的類別和函式。
TypeVar
1from typing import TypeVar, List
2
3T = TypeVar("T")
4
5def first_element(items: List[T]) -> T:
6 """返回列表的第一個元素"""
7 return items[0]
8
9# 使用時會推斷型別
10names = ["Alice", "Bob"]
11first_name = first_element(names) # 推斷為 str
12
13numbers = [1, 2, 3]
14first_num = first_element(numbers) # 推斷為 int有限制的 TypeVar
1from typing import TypeVar
2
3# 只能是 str 或 bytes
4StrOrBytes = TypeVar("StrOrBytes", str, bytes)
5
6def process(data: StrOrBytes) -> StrOrBytes:
7 return data.strip() # str 和 bytes 都有 strip()實際範例:Hook 輸出建立
來自 .claude/lib/hook_io.py:
1from typing import Any, Optional
2
3def create_pretooluse_output(
4 decision: str,
5 reason: str,
6 user_prompt: Optional[str] = None,
7 system_message: Optional[str] = None,
8 suppress_output: bool = False
9) -> dict:
10 """建立 PreToolUse Hook 輸出格式"""
11
12 # 使用 dict[str, Any] 表示值可以是任意型別
13 output: dict[str, Any] = {
14 "hookSpecificOutput": {
15 "hookEventName": "PreToolUse",
16 "permissionDecision": decision,
17 "permissionDecisionReason": reason
18 }
19 }
20
21 # Optional 參數的處理
22 if user_prompt:
23 output["hookSpecificOutput"]["userPrompt"] = user_prompt
24
25 if system_message:
26 output["systemMessage"] = system_message
27
28 if suppress_output:
29 output["suppressOutput"] = True
30
31 return outputAny
Any 表示任意型別,相當於關閉型別檢查。
1from typing import Any
2
3def log_value(value: Any) -> None:
4 """記錄任意型別的值"""
5 print(f"Value: {value}")
6
7# 字典值為任意型別
8config: dict[str, Any] = {
9 "name": "test", # str
10 "count": 10, # int
11 "enabled": True, # bool
12 "items": [1, 2, 3] # list
13}何時使用 Any?
- 處理動態資料(如 JSON)
- 與外部 API 互動
- 型別過於複雜難以表達
注意:過度使用 Any 會降低型別檢查的效果。
Callable
表示可呼叫物件(函式、方法、lambda)。
1from typing import Callable
2
3def apply_operation(
4 value: int,
5 operation: Callable[[int], int]
6) -> int:
7 """對值應用操作"""
8 return operation(value)
9
10# 使用
11result = apply_operation(5, lambda x: x * 2) # 10Python 3.9+ 語法
從 Python 3.9 開始,可以直接使用內建型別:
1# Python 3.9+
2def process(items: list[str]) -> dict[str, int]:
3 ...
4
5# Python 3.8 及之前需要導入
6from typing import List, Dict
7def process(items: List[str]) -> Dict[str, int]:
8 ...從 Python 3.10 開始,可以使用 | 語法:
1# Python 3.10+
2def find(id: int) -> User | None:
3 ...
4
5# Python 3.9 及之前
6from typing import Optional
7def find(id: int) -> Optional[User]:
8 ...常見模式
可選參數
1def setup_logging(
2 name: str,
3 level: Optional[int] = None, # 可以是 int 或 None
4 path: Optional[str] = None # 可以是 str 或 None
5) -> Logger:
6 if level is None:
7 level = logging.INFO
8 ...返回值可能為空
1def find_config(name: str) -> Optional[dict]:
2 """找不到配置時返回 None"""
3 path = get_config_path(name)
4 if not path.exists():
5 return None
6 return load_config(path)字典值型別不確定
1from typing import Any
2
3def parse_json(text: str) -> dict[str, Any]:
4 """JSON 值可能是任意型別"""
5 return json.loads(text)思考題
Optional[str]和str | None有什麼區別?- 什麼時候應該用
Any?什麼時候應該避免? - 如何為一個接受任意可迭代物件的函式添加型別提示?
實作練習
為以下函式添加型別提示:
1def get_value(data, key, default=None): 2 return data.get(key, default)寫一個泛型函式,返回列表中的最大和最小值
為一個回調函式參數添加
Callable型別提示
延伸閱讀(進階系列)
上一章:Type Hints 基礎 下一章:Dataclass 資料結構