python-type-safety
型ヒント、ジェネリクス、プロトコル、厳格な型チェックを活用したPythonの型安全性を担当するスキルです。型アノテーションの追加、ジェネリッククラスの実装、構造的インターフェースの定義、またはmypy/pyrightの設定を行う際に使用してください。
description の原文を見る
Python type safety with type hints, generics, protocols, and strict type checking. Use when adding type annotations, implementing generic classes, defining structural interfaces, or configuring mypy/pyright.
SKILL.md 本文
Python 型安全性
Pythonの型システムを活用して、静的解析時にエラーを検出します。型アノテーションは、ツールが自動的に検証する強制的なドキュメントとして機能します。
このスキルを使う場合
- 既存コードへの型ヒント追加
- ジェネリック、再利用可能なクラスの作成
- Protocol を使った構造的インターフェースの定義
- mypy または pyright の厳密チェック設定
- 型の絞り込みとガードの理解
- 型安全な API やライブラリの構築
コア コンセプト
1. 型アノテーション
関数パラメータ、戻り値、変数の期待される型を宣言します。
2. ジェネリック
異なる型間で型情報を保持する再利用可能なコードを記述します。
3. Protocol
継承なしで構造的インターフェースを定義します(型安全ダックタイピング)。
4. 型の絞り込み
ガードと条件分岐を使用して、コードブロック内で型を絞り込みます。
クイック スタート
def get_user(user_id: str) -> User | None:
"""Return type makes 'might not exist' explicit."""
...
# Type checker enforces handling None case
user = get_user("123")
if user is None:
raise UserNotFoundError("123")
print(user.name) # Type checker knows user is User here
基本パターン
パターン 1: すべての公開シグネチャに型アノテーションを付ける
すべての公開関数、メソッド、クラスは型アノテーションを持つべきです。
def get_user(user_id: str) -> User:
"""Retrieve user by ID."""
...
def process_batch(
items: list[Item],
max_workers: int = 4,
) -> BatchResult[ProcessedItem]:
"""Process items concurrently."""
...
class UserRepository:
def __init__(self, db: Database) -> None:
self._db = db
async def find_by_id(self, user_id: str) -> User | None:
"""Return User if found, None otherwise."""
...
async def find_by_email(self, email: str) -> User | None:
...
async def save(self, user: User) -> User:
"""Save and return user with generated ID."""
...
CI で mypy --strict または pyright を使用して、型エラーを早期に検出します。既存プロジェクトの場合、モジュール単位のオーバーライドを使って段階的に厳密モードを有効化してください。
パターン 2: モダン Union 構文を使う
Python 3.10+ はより簡潔な union 構文を提供します。
# Preferred (3.10+)
def find_user(user_id: str) -> User | None:
...
def parse_value(v: str) -> int | float | str:
...
# Older style (still valid, needed for 3.9)
from typing import Optional, Union
def find_user(user_id: str) -> Optional[User]:
...
パターン 3: ガードを使った型の絞り込み
型チェッカー用に条件分岐で型を絞り込みます。
def process_user(user_id: str) -> UserData:
user = find_user(user_id)
if user is None:
raise UserNotFoundError(f"User {user_id} not found")
# Type checker knows user is User here, not User | None
return UserData(
name=user.name,
email=user.email,
)
def process_items(items: list[Item | None]) -> list[ProcessedItem]:
# Filter and narrow types
valid_items = [item for item in items if item is not None]
# valid_items is now list[Item]
return [process(item) for item in valid_items]
パターン 4: ジェネリック クラス
型安全な再利用可能なコンテナを作成します。
from typing import TypeVar, Generic
T = TypeVar("T")
E = TypeVar("E", bound=Exception)
class Result(Generic[T, E]):
"""Represents either a success value or an error."""
def __init__(
self,
value: T | None = None,
error: E | None = None,
) -> None:
if (value is None) == (error is None):
raise ValueError("Exactly one of value or error must be set")
self._value = value
self._error = error
@property
def is_success(self) -> bool:
return self._error is None
@property
def is_failure(self) -> bool:
return self._error is not None
def unwrap(self) -> T:
"""Get value or raise the error."""
if self._error is not None:
raise self._error
return self._value # type: ignore[return-value]
def unwrap_or(self, default: T) -> T:
"""Get value or return default."""
if self._error is not None:
return default
return self._value # type: ignore[return-value]
# Usage preserves types
def parse_config(path: str) -> Result[Config, ConfigError]:
try:
return Result(value=Config.from_file(path))
except ConfigError as e:
return Result(error=e)
result = parse_config("config.yaml")
if result.is_success:
config = result.unwrap() # Type: Config
高度なパターン
パターン 5: ジェネリック リポジトリ
型安全なデータアクセス パターンを作成します。
from typing import TypeVar, Generic
from abc import ABC, abstractmethod
T = TypeVar("T")
ID = TypeVar("ID")
class Repository(ABC, Generic[T, ID]):
"""Generic repository interface."""
@abstractmethod
async def get(self, id: ID) -> T | None:
"""Get entity by ID."""
...
@abstractmethod
async def save(self, entity: T) -> T:
"""Save and return entity."""
...
@abstractmethod
async def delete(self, id: ID) -> bool:
"""Delete entity, return True if existed."""
...
class UserRepository(Repository[User, str]):
"""Concrete repository for Users with string IDs."""
async def get(self, id: str) -> User | None:
row = await self._db.fetchrow(
"SELECT * FROM users WHERE id = $1", id
)
return User(**row) if row else None
async def save(self, entity: User) -> User:
...
async def delete(self, id: str) -> bool:
...
パターン 6: TypeVar に制約を付ける
ジェネリック パラメータを特定の型に制限します。
from typing import TypeVar
from pydantic import BaseModel
ModelT = TypeVar("ModelT", bound=BaseModel)
def validate_and_create(model_cls: type[ModelT], data: dict) -> ModelT:
"""Create a validated Pydantic model from dict."""
return model_cls.model_validate(data)
# Works with any BaseModel subclass
class User(BaseModel):
name: str
email: str
user = validate_and_create(User, {"name": "Alice", "email": "a@b.com"})
# user is typed as User
# Type error: str is not a BaseModel subclass
result = validate_and_create(str, {"name": "Alice"}) # Error!
パターン 7: 構造的型付けのための Protocol
継承を必要とせずにインターフェースを定義します。
from typing import Protocol, runtime_checkable
@runtime_checkable
class Serializable(Protocol):
"""Any class that can be serialized to/from dict."""
def to_dict(self) -> dict:
...
@classmethod
def from_dict(cls, data: dict) -> "Serializable":
...
# User satisfies Serializable without inheriting from it
class User:
def __init__(self, id: str, name: str) -> None:
self.id = id
self.name = name
def to_dict(self) -> dict:
return {"id": self.id, "name": self.name}
@classmethod
def from_dict(cls, data: dict) -> "User":
return cls(id=data["id"], name=data["name"])
def serialize(obj: Serializable) -> str:
"""Works with any Serializable object."""
return json.dumps(obj.to_dict())
# Works - User matches the protocol
serialize(User("1", "Alice"))
# Runtime checking with @runtime_checkable
isinstance(User("1", "Alice"), Serializable) # True
パターン 8: 一般的な Protocol パターン
再利用可能な構造的インターフェースを定義します。
from typing import Protocol
class Closeable(Protocol):
"""Resource that can be closed."""
def close(self) -> None: ...
class AsyncCloseable(Protocol):
"""Async resource that can be closed."""
async def close(self) -> None: ...
class Readable(Protocol):
"""Object that can be read from."""
def read(self, n: int = -1) -> bytes: ...
class HasId(Protocol):
"""Object with an ID property."""
@property
def id(self) -> str: ...
class Comparable(Protocol):
"""Object that supports comparison."""
def __lt__(self, other: "Comparable") -> bool: ...
def __le__(self, other: "Comparable") -> bool: ...
パターン 9: 型エイリアス
意味のある型名を作成します。
注: type Alias = ... ステートメント構文(PEP 695)は Python 3.12 で導入されました。3.10/3.11 を含む以前のバージョンをターゲットとするプロジェクトでは、TypeAlias アノテーション(PEP 613、Python 3.10 以降で利用可能)を使用してください。
# Python 3.12+ type statement (PEP 695)
type UserId = str
type UserDict = dict[str, Any]
# Python 3.12+ type statement with generics (PEP 695)
type Handler[T] = Callable[[Request], T]
type AsyncHandler[T] = Callable[[Request], Awaitable[T]]
# Python 3.10-3.11 style (needed for broader compatibility)
from typing import TypeAlias
from collections.abc import Callable, Awaitable
UserId: TypeAlias = str
Handler: TypeAlias = Callable[[Request], Response]
# Usage
def register_handler(path: str, handler: Handler[Response]) -> None:
...
パターン 10: Callable 型
関数パラメータとコールバックに型を付けます。
from collections.abc import Callable, Awaitable
# Sync callback
ProgressCallback = Callable[[int, int], None] # (current, total)
# Async callback
AsyncHandler = Callable[[Request], Awaitable[Response]]
# With named parameters (using Protocol)
class OnProgress(Protocol):
def __call__(
self,
current: int,
total: int,
*,
message: str = "",
) -> None: ...
def process_items(
items: list[Item],
on_progress: ProgressCallback | None = None,
) -> list[Result]:
for i, item in enumerate(items):
if on_progress:
on_progress(i, len(items))
...
設定
厳密モード チェックリスト
mypy --strict コンプライアンスの場合:
# pyproject.toml
[tool.mypy]
python_version = "3.12"
strict = true
warn_return_any = true
warn_unused_ignores = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
no_implicit_optional = true
段階的導入の目標:
- すべての関数パラメータにアノテーション
- すべての戻り値型にアノテーション
- クラス属性にアノテーション
Any使用を最小化(本当に動的なデータの場合は許容)- ジェネリック コレクションは型パラメータを使用(
listではなくlist[str])
既存コードベースの場合、# mypy: strict を使うか、pyproject.toml でモジュール単位のオーバーライドを設定して、モジュール単位で厳密モードを有効化します。
ベスト プラクティス サマリー
- すべての公開 API にアノテーション - 関数、メソッド、クラス属性
T | Noneを使用 -Optional[T]よりもモダン union 構文- 厳密な型チェックを実行 - CI で
mypy --strict - ジェネリックを使用 - 再利用可能なコードで型情報を保持
- Protocol を定義 - インターフェース用の構造的型付け
- 型を絞り込む - ガードを使って型チェッカーを支援
- 型変数に制約を付ける - ジェネリックを意味のある型に制限
- 型エイリアスを作成 - 複雑な型に意味のある名前を付ける
Anyを最小化 - 特定の型またはジェネリックを使用。本当に動的なデータまたは型なしサードパーティコードとのインターフェース時はAnyが許容可能- 型でドキュメント化 - 型は強制的なドキュメント
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- wshobson
- リポジトリ
- wshobson/agents
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/wshobson/agents / ライセンス: MIT
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。