claude-code/claude-zh/skills/python-patterns/SKILL.md

544 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: python-patterns
description: Pythonic 語法習慣、PEP 8 標準、型別提示 (Type Hints) 以及建構強韌、高效且易於維護的 Python 應用程式之最佳實踐。
---
# Python 開發模式 (Python Development Patterns)
用於建構強韌、高效且易於維護的應用程式之道地 Python 模式與最佳實踐。
## 何時啟用
- 編寫新的 Python 程式碼。
- 審查 Python 程式碼。
- 重構現有的 Python 程式碼。
- 設計 Python 套件或模組。
## 核心原則
### 1. 可讀性至上 (Readability Counts)
Python 優先考慮可讀性。程式碼應當顯而易見且易於理解。
```python
# 正確:清晰且具備可讀性
def get_active_users(users: list[User]) -> list[User]:
"""從提供的清單中僅回傳活躍的使用者。"""
return [user for user in users if user.is_active]
# 錯誤:雖然取巧但令人困惑
def get_active_users(u):
return [x for x in u if x.a]
```
### 2. 明示優於暗示 (Explicit is Better Than Implicit)
避免使用神祕的機制;應明確表達程式碼的功能。
```python
# 正確:明示配置資訊
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# 錯誤:隱藏的副作用
import some_module
some_module.setup() # 這具體做了什麼?
```
### 3. EAFP 模式 — 請求原諒比請求許可更容易
Python 傾向於使用例外處理,而非預先檢查條件。
```python
# 正確EAFP 風格
def get_value(dictionary: dict, key: str) -> Any:
try:
return dictionary[key]
except KeyError:
return default_value
# 錯誤LBYL (三思而後行) 風格
def get_value(dictionary: dict, key: str) -> Any:
if key in dictionary:
return dictionary[key]
else:
return default_value
```
## 型別提示 (Type Hints)
### 基礎型別註解
```python
from typing import Optional, List, Dict, Any
def process_user(
user_id: str,
data: Dict[str, Any],
active: bool = True
) -> Optional[User]:
"""處理使用者並回傳更新後的使用者物件或 None。"""
if not active:
return None
return User(user_id, data)
```
### 現代型別提示 (Python 3.9+)
```python
# Python 3.9+ - 直接使用內建型別
def process_items(items: list[str]) -> dict[str, int]:
return {item: len(item) for item in items}
# Python 3.8 及更早版本 - 需使用 typing 模組
from typing import List, Dict
def process_items(items: List[str]) -> Dict[str, int]:
return {item: len(item) for item in items}
```
### 型別別名與 TypeVar
```python
from typing import TypeVar, Union
# 針對複雜型別建立別名
JSON = Union[dict[str, Any], list[Any], str, int, float, bool, None]
def parse_json(data: str) -> JSON:
return json.loads(data)
# 泛型型別
T = TypeVar('T')
def first(items: list[T]) -> T | None:
"""回傳第一個項目,若清單為空則回傳 None。"""
return items[0] if items else None
```
### 基於 Protocol 的鴨子型別 (Duck Typing)
```python
from typing import Protocol
class Renderable(Protocol):
def render(self) -> str:
"""將物件渲染為字串。"""
def render_all(items: list[Renderable]) -> str:
"""渲染所有實作了 Renderable 協定的項目。"""
return "\n".join(item.render() for item in items)
```
## 例外處理模式
### 具體的例外處理
```python
# 正確:捕捉特定的例外
def load_config(path: str) -> Config:
try:
with open(path) as f:
return Config.from_json(f.read())
except FileNotFoundError as e:
raise ConfigError(f"找不到配置檔案: {path}") from e
except json.JSONDecodeError as e:
raise ConfigError(f"配置檔案中存在無效的 JSON: {path}") from e
# 錯誤:寬泛的 except
def load_config(path: str) -> Config:
try:
with open(path) as f:
return Config.from_json(f.read())
except:
return None # 靜默失敗!
```
### 例外鏈 (Exception Chaining)
```python
def process_data(data: str) -> Result:
try:
parsed = json.loads(data)
except json.JSONDecodeError as e:
# 使用 'from e' 鏈結例外以保留堆疊追蹤資訊
raise ValueError(f"解析資料失敗: {data}") from e
```
### 自定義例外階層
```python
class AppError(Exception):
"""所有應用程式錯誤的基底例外。"""
pass
class ValidationError(AppError):
"""當輸入驗證失敗時拋出。"""
pass
class NotFoundError(AppError):
"""當找不到請求的資源時拋出。"""
pass
# 使用方式
def get_user(user_id: str) -> User:
user = db.find_user(user_id)
if not user:
raise NotFoundError(f"找不到使用者: {user_id}")
return user
```
## 上下文管理器 (Context Managers)
### 資源管理
```python
# 正確:使用上下文管理器
def process_file(path: str) -> str:
with open(path, 'r') as f:
return f.read()
# 錯誤:手動管理資源
def process_file(path: str) -> str:
f = open(path, 'r')
try:
return f.read()
finally:
f.close()
```
### 自定義上下文管理器
```python
from contextlib import contextmanager
import time
@contextmanager
def timer(name: str):
"""用於計算程式區塊執行時間的上下文管理器。"""
start = time.perf_counter()
yield
elapsed = time.perf_counter() - start
print(f"{name} 耗時 {elapsed:.4f} 秒")
# 使用方式
with timer("資料處理"):
process_large_dataset()
```
## 解析式 (Comprehensions) 與產生器 (Generators)
### 清單解析式
```python
# 正確:使用清單解析式進行簡易轉換
names = [user.name for user in users if user.is_active]
# 錯誤:手動迴圈
names = []
for user in users:
if user.is_active:
names.append(user.name)
# 過於複雜的解析式應展開處理
# 錯誤:過於複雜
result = [x * 2 for x in items if x > 0 if x % 2 == 0]
# 正確:使用產生器函式或展開迴圈以利閱讀
def filter_and_transform(items: Iterable[int]) -> list[int]:
result = []
for x in items:
if x > 0 and x % 2 == 0:
result.append(x * 2)
return result
```
### 產生器表達式
```python
# 正確:使用產生器進行惰性求值
total = sum(x * x for x in range(1_000_000))
# 錯誤:建立了一個巨大的中間清單
total = sum([x * x for x in range(1_000_000)])
```
### 產生器函式
```python
def read_large_file(path: str) -> Iterator[str]:
"""逐行讀取大型檔案。"""
with open(path) as f:
for line in f:
yield line.strip()
# 使用方式
for line in read_large_file("huge.txt"):
process(line)
```
## 資料類別 (Data Classes) 與具名元組 (Named Tuples)
### 資料類別 (Data Classes)
```python
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class User:
"""使用者實體,自動生成 __init__, __repr__, 與 __eq__。"""
id: str
name: str
email: str
created_at: datetime = field(default_factory=datetime.now)
is_active: bool = True
# 使用方式
user = User(
id="123",
name="Alice",
email="alice@example.com"
)
```
### 具名元組 (Named Tuples)
```python
from typing import NamedTuple
class Point(NamedTuple):
"""不可變的 2D 座標點。"""
x: float
y: float
def distance(self, other: 'Point') -> float:
return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5
# 使用方式
p1 = Point(0, 0)
p2 = Point(3, 4)
print(p1.distance(p2)) # 5.0
```
## 裝飾器 (Decorators)
### 函式裝飾器
```python
import functools
import time
from typing import Callable
def timer(func: Callable) -> Callable:
"""用於計時函式執行時間的裝飾器。"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} 執行耗時 {elapsed:.4f} 秒")
return result
return wrapper
```
## 並發模式 (Concurrency Patterns)
### I/O 密集型任務使用執行緒 (Threading)
```python
import concurrent.futures
def fetch_url(url: str) -> str:
"""擷取 URL 內容I/O 密集型操作)。"""
import urllib.request
with urllib.request.urlopen(url) as response:
return response.read().decode()
def fetch_all_urls(urls: list[str]) -> dict[str, str]:
"""使用執行緒池並行擷取多個 URL。"""
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
future_to_url = {executor.submit(fetch_url, url): url for url in urls}
results = {}
for future in concurrent.futures.as_completed(future_to_url):
url = future_to_url[future]
try:
results[url] = future.result()
except Exception as e:
results[url] = f"錯誤: {e}"
return results
```
### CPU 密集型任務使用多進程 (Multiprocessing)
```python
def process_data(data: list[int]) -> int:
"""CPU 密集型計算。"""
return sum(x ** 2 for x in data)
def process_all(datasets: list[list[int]]) -> list[int]:
"""使用多個進程處理多個資料集。"""
with concurrent.futures.ProcessPoolExecutor() as executor:
results = list(executor.map(process_data, datasets))
return results
```
## 專案與套件組織
### 標準專案佈局
```
myproject/
├── src/ # 原始碼目錄
│ └── mypackage/
│ ├── __init__.py
│ ├── main.py
│ ├── api/
│ │ ├── __init__.py
│ │ └── routes.py
│ ├── models/
│ │ ├── __init__.py
│ │ └── user.py
│ └── utils/
│ ├── __init__.py
│ └── helpers.py
├── tests/ # 測試目錄
│ ├── __init__.py
│ ├── conftest.py
│ ├── test_api.py
│ └── test_models.py
├── pyproject.toml # 專案配置設定
├── README.md
└── .gitignore
```
### 匯入慣例 (Import Conventions)
```python
# 正确:匯入順序 — 標準函式庫、第三方套件、本地模組
import os
import sys
from pathlib import Path
import requests
from fastapi import FastAPI
from mypackage.models import User
from mypackage.utils import format_name
```
## 記憶體與效能
### 使用 __slots__ 提升記憶體效率
```python
# 錯誤:一般類別使用 __dict__耗費更多記憶體
class Point:
def __init__(self, x: float, y: float):
self.x = x
self.y = y
# 正確:使用 __slots__ 減少記憶體消耗
class Point:
__slots__ = ['x', 'y']
def __init__(self, x: float, y: float):
self.x = x
self.y = y
```
### 避免在迴圈中進行字串拼接
```python
# 錯誤:字串不可變性導致 O(n²) 複雜度
result = ""
for item in items:
result += str(item)
# 正確:使用 join 達成 O(n) 複雜度
result = "".join(str(item) for item in items)
```
## Python 工具整合
### 必要指令
```bash
# 程式碼格式化
black .
isort .
# 靜態分析 (Linter)
ruff check .
pylint mypackage/
# 型別檢查
mypy .
# 執行測試
pytest --cov=mypackage --cov-report=html
# 安全性掃描
bandit -r .
```
## 常見語法習慣 (Idioms) 快速參考
| 語法習慣 | 說明描述 |
|-------|-------------|
| **EAFP** | 請求原諒比請求許可更容易 |
| **上下文管理器** | 使用 `with` 語句管理資源 |
| **清單解析式** | 用於簡易的資料轉換 |
| **產生器** | 用於惰性求值與大型資料集處理 |
| **型別提示** | 註解函式簽名以提升安全性 |
| **資料類別** | 提供自動生成方法的資料容器 |
| **__slots__** | 優化物件記憶體占用 |
| **f-strings** | 引進自 Python 3.6 的強化字串格式化 |
| **pathlib.Path** | 引進自 Python 3.4 的物件導向路徑操作 |
| **enumerate** | 在迴圈中同時獲取索引與元素 |
## 應避免的反模式
```python
# 錯誤:可變物件作為預設參數 (Mutable default arguments)
def append_to(item, items=[]):
items.append(item)
return items
# 正確:使用 None 並在函式內部建立新清單
def append_to(item, items=None):
if items is None:
items = []
items.append(item)
return items
# 錯誤:使用 type() 檢查型別
if type(obj) == list:
process(obj)
# 正確:使用 isinstance
if isinstance(obj, list):
process(obj)
# 錯誤:使用 == 與 None 進行比較
if value == None:
process()
# 正確:使用 is 判斷身分
if value is None:
process()
# 錯誤:使用星號匯入 (import *)
from os.path import *
# 正確:明確匯入所需項目
from os.path import join, exists
```
**請記住**Python 程式碼應具備可讀性、明確性,並遵循「最小驚訝原則」。當遇到疑慮時,應優先考慮清晰度而非技術上的取巧。