mypy について
mypy は、Python の動的な特性を保ちながら型ヒントを段階的に導入したい開発者向けの静的型チェッカーです。厳格な検査、型推論、プラグイン、増分チェックに対応します。
主な機能
- 01型ヒントの段階的な導入
- 02厳格な型チェックモード
- 03型推論と構造的部分型
- 04ジェネリクスと高度な型パターン
- 05プラグインによる検査の拡張
- 06大規模コードベースの増分チェック
活用シーン
- 01実行前に型エラーを検出
- 02既存の Python プロジェクトに型を段階的に追加
- 03FastAPI または Django プロジェクトの型検査
- 04CI や pre-commit フックで検査を実行
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ bobmatnyc/ claude-mpm-skills --skill mypyインストール後、Claude Code で/mypyを入力して読み込まれたか確認します。
同梱ファイルmetadata.json
詳細
mypy - Python 用静的型チェッカー
概要
mypy は Python の標準的な静的型チェッカーです。型ヒント(PEP 484)による段階的な型付けと、包括的な型安全性を実現します。実行前に型エラーを検出し、コードのドキュメント性を高め、IDE のサポートを強化しながら、段階的な導入によって Python の動的な性質を維持します。
主な機能:
- 段階的な型付け:既存のコードに型を少しずつ追加できます
- 厳格モード:
--strictフラグで最大限の型安全性を実現します - 型推論:コンテキストから型を自動的に推論します
- プロトコルのサポート:構造的部分型付け(型によるダックタイピング)
- ジェネリック型:TypeVar、Generic、高度な型パターン
- フレームワークとの統合:FastAPI、Django、Pydantic に対応
- プラグインシステム:ライブラリ向けに型チェックを拡張できます
- 差分チェック:大規模なコードベースでも高速に型チェックできます
インストール:
# Basic mypy
pip install mypy
# With common type stubs
pip install mypy types-requests types-PyYAML types-redis
# For FastAPI projects
pip install mypy pydantic
# For Django projects
pip install mypy django-stubs
# Development setup
pip install mypy pre-commit
型アノテーションの基本
1. 変数の型ヒント
# Basic types
name: str = "Alice"
age: int = 30
height: float = 5.9
is_active: bool = True
# Type inference (mypy infers types)
count = 10 # mypy infers: int
message = "Hello" # mypy infers: str
# Multiple types with Union
from typing import Union
user_id: Union[int, str] = 123 # Can be int OR str
result: Union[int, None] = None # Nullable int
# Optional (shorthand for Union[T, None])
from typing import Optional
user_email: Optional[str] = None # Can be str or None
2. 関数の型ヒント
# Basic function typing
def greet(name: str) -> str:
return f"Hello, {name}"
# Multiple parameters
def add(a: int, b: int) -> int:
return a + b
# Optional parameters with defaults
def create_user(name: str, age: int = 18) -> dict:
return {"name": name, "age": age}
# No return value
def log_message(message: str) -> None:
print(message)
# Functions that never return
from typing import NoReturn
def raise_error() -> NoReturn:
raise ValueError("Always raises")
3. コレクションの型ヒント
from typing import List, Dict, Set, Tuple
# List with element type
numbers: List[int] = [1, 2, 3, 4]
names: List[str] = ["Alice", "Bob", "Charlie"]
# Dict with key and value types
user_ages: Dict[str, int] = {"Alice": 30, "Bob": 25}
config: Dict[str, Union[str, int]] = {"host": "localhost", "port": 8000}
# Set with element type
unique_ids: Set[int] = {1, 2, 3}
# Tuple with fixed types
coordinate: Tuple[float, float] = (10.5, 20.3)
user_record: Tuple[int, str, bool] = (1, "Alice", True)
# Variable-length tuple
numbers: Tuple[int, ...] = (1, 2, 3, 4, 5)
# Modern syntax (Python 3.9+)
numbers: list[int] = [1, 2, 3]
user_ages: dict[str, int] = {"Alice": 30}
4. クラスの型ヒント
class User:
# Class attributes
name: str
age: int
email: Optional[str]
def __init__(self, name: str, age: int, email: Optional[str] = None) -> None:
self.name = name
self.age = age
self.email = email
def get_info(self) -> Dict[str, Union[str, int]]:
return {
"name": self.name,
"age": self.age,
"email": self.email or "N/A"
}
@classmethod
def from_dict(cls, data: Dict[str, any]) -> "User":
return cls(
name=data["name"],
age=data["age"],
email=data.get("email")
)
高度な型ヒント
1. リテラル型
from typing import Literal
# Restrict to specific values
def set_log_level(level: Literal["debug", "info", "warning", "error"]) -> None:
print(f"Log level: {level}")
# Valid
set_log_level("debug")
set_log_level("error")
# Type error: Argument 1 has incompatible type "verbose"
set_log_level("verbose")
# Multiple literals
Status = Literal["pending", "approved", "rejected"]
def update_status(status: Status) -> None:
pass
2. 型エイリアス
from typing import Dict, List, Union
# Simple alias
UserId = int
UserName = str
def get_user(user_id: UserId) -> UserName:
return f"User {user_id}"
# Complex aliases
JSON = Union[Dict[str, "JSON"], List["JSON"], str, int, float, bool, None]
Headers = Dict[str, str]
QueryParams = Dict[str, Union[str, int, List[str]]]
def make_request(
url: str,
headers: Headers,
params: QueryParams
) -> JSON:
pass
# NewType for distinct types
from typing import NewType
UserId = NewType("UserId", int)
ProductId = NewType("ProductId", int)
def get_user(user_id: UserId) -> str:
return f"User {user_id}"
user = UserId(123) # Valid
product = ProductId(456)
get_user(user) # Valid
get_user(product) # Type error: ProductId not compatible with UserId
3. ジェネリクスと TypeVar
from typing import TypeVar, Generic, List
# TypeVar for generic functions
T = TypeVar("T")
def first_element(items: List[T]) -> T:
return items[0]
# Type inference
num = first_element([1, 2, 3]) # mypy infers: int
name = first_element(["Alice", "Bob"]) # mypy infers: str
# Bounded TypeVar
from typing import Union
NumericType = TypeVar("NumericType", int, float)
def add_numbers(a: NumericType, b: NumericType) -> NumericType:
return a + b
# Generic classes
class Stack(Generic[T]):
def __init__(self) -> None:
self._items: List[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
def is_empty(self) -> bool:
return len(self._items) == 0
# Usage with type inference
int_stack: Stack[int] = Stack()
int_stack.push(42)
int_stack.push("hello") # Type error: Expected int, got str
str_stack: Stack[str] = Stack()
str_stack.push("hello") # Valid
4. Protocol(構造的部分型付け)
from typing import Protocol
# Define protocol (interface)
class Drawable(Protocol):
def draw(self) -> str:
...
# Any class with draw() method matches
class Circle:
def draw(self) -> str:
return "Drawing circle"
class Square:
def draw(self) -> str:
return "Drawing square"
# Function accepts any Drawable
def render(obj: Drawable) -> str:
return obj.draw()
# Both work (duck typing with types)
circle = Circle()
square = Square()
render(circle) # Valid
render(square) # Valid
# Runtime checkable protocols
from typing import runtime_checkable
@runtime_checkable
class Closeable(Protocol):
def close(self) -> None:
...
class File:
def close(self) -> None:
pass
# Runtime check
f = File()
isinstance(f, Closeable) # True
5. Callable 型
from typing import Callable
# Function that takes another function
def apply_twice(func: Callable[[int], int], value: int) -> int:
return func(func(value))
def double(x: int) -> int:
return x * 2
result = apply_twice(double, 5) # Returns 20
# Generic callable
from typing import TypeVar
T = TypeVar("T")
R = TypeVar("R")
def map_values(
func: Callable[[T], R],
values: List[T]
) -> List[R]:
return [func(v) for v in values]
# Callable with multiple arguments
Validator = Callable[[str, int], bool]
def validate_user(name: str, age: int) -> bool:
return len(name) > 0 and age >= 0
validator: Validator = validate_user
mypy の設定
1. mypy.ini の設定
# mypy.ini
[mypy]
# Python version
python_version = 3.11
# Import discovery
files = src,tests
exclude = build,dist,venv
# Type checking strictness
disallow_untyped_defs = True
disallow_any_unimported = False
no_implicit_optional = True
warn_return_any = True
warn_unused_ignores = True
warn_redundant_casts = True
# Error reporting
show_error_codes = True
show_column_numbers = True
pretty = True
# Incremental type checking
incremental = True
cache_dir = .mypy_cache
# Per-module configuration
[mypy-tests.*]
disallow_untyped_defs = False
[mypy-migrations.*]
ignore_errors = True
# Third-party libraries without stubs
[mypy-redis.*]
ignore_missing_imports = True
[mypy-celery.*]
ignore_missing_imports = True
2. pyproject.toml の設定
# pyproject.toml
[tool.mypy]
python_version = "3.11"
files = ["src", "tests"]
exclude = ["build", "dist", "venv"]
# Strictness
disallow_untyped_defs = true
disallow_any_unimported = false
no_implicit_optional = true
warn_return_any = true
warn_unused_ignores = true
warn_redundant_casts = true
strict_equality = true
strict_concatenate = true
# Error reporting
show_error_codes = true
show_column_numbers = true
pretty = true
color_output = true
# Incremental
incremental = true
cache_dir = ".mypy_cache"
# Per-module overrides
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
[[tool.mypy.overrides]]
module = ["redis.*", "celery.*"]
ignore_missing_imports = true
3. 厳格モード
# Enable all strict checks
mypy --strict src/
# Strict mode equivalent flags
mypy \
--disallow-any-unimported \
--disallow-any-expr \
--disallow-any-decorated \
--disallow-any-explicit \
--disallow-any-generics \
--disallow-subclassing-any \
--disallow-untyped-calls \
--disallow-untyped-defs \
--disallow-incomplete-defs \
--check-untyped-defs \
--disallow-untyped-decorators \
--no-implicit-optional \
--warn-redundant-casts \
--warn-unused-ignores \
--warn-return-any \
--warn-unreachable \
--strict-equality \
src/
# mypy.ini strict configuration
[mypy]
strict = True
# Relax specific checks if needed
disallow_any_expr = False # Too strict for most projects
disallow_any_explicit = False # Allow explicit Any
段階的な導入方法
1. エントリーポイントから始める
# Start typing from main.py (top-level)
# main.py
from typing import Optional
from app.services import UserService
def main(config_path: Optional[str] = None) -> None:
"""Application entry point."""
service = UserService()
service.run()
if __name__ == "__main__":
main()
# Check only main.py initially
mypy main.py
# Gradually expand scope
mypy main.py app/services.py
mypy src/
2. モジュールごとの厳格モード
# mypy.ini - Gradually enable strict checking
[mypy]
# Lenient global defaults
ignore_missing_imports = True
disallow_untyped_defs = False
# Strict for new modules
[mypy-app.services.user_service]
disallow_untyped_defs = True
warn_return_any = True
[mypy-app.api.*]
disallow_untyped_defs = True
no_implicit_optional = True
# Still lenient for legacy code
[mypy-app.legacy.*]
ignore_errors = True
3. # type: ignore を適切に使う
# Suppress specific errors during migration
import legacy_module # type: ignore[import]
def process_data(data): # type: ignore[no-untyped-def]
# TODO: Add type hints
return data.transform()
# Ignore specific error codes
user_dict = get_user_dict()
user_id = user_dict["id"] # type: ignore[index]
# Ignore entire line (use sparingly)
result = external_api.call() # type: ignore
4. 開発中に型を表示する
from typing import reveal_type
def process_user(user_id: int):
user = get_user(user_id)
reveal_type(user) # mypy will show inferred type
name = user.name
reveal_type(name) # mypy will show: str
FastAPI との統合
1. 型ヒントを使った FastAPI
# main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
app = FastAPI()
# Pydantic models (auto-validated)
class User(BaseModel):
id: int
name: str
email: str
age: Optional[int] = None
class UserCreate(BaseModel):
name: str
email: str
age: Optional[int] = None
# Type-safe endpoints
@app.get("/")
def read_root() -> dict[str, str]:
return {"message": "Hello World"}
@app.get("/users/{user_id}")
def read_user(user_id: int) -> User:
if user_id == 0:
raise HTTPException(status_code=404, detail="User not found")
return User(id=user_id, name=f"User {user_id}", email="[email protected]")
@app.get("/users")
def list_users(skip: int = 0, limit: int = 10) -> List[User]:
users = [
User(id=i, name=f"User {i}", email=f"user{i}@example.com")
for i in range(skip, skip + limit)
]
return users
@app.post("/users")
def create_user(user: UserCreate) -> User:
# Pydantic ensures type safety
return User(id=1, name=user.name, email=user.email, age=user.age)
2. 非同期 FastAPI の型チェック
from typing import List, Optional
from fastapi import FastAPI, Depends
from sqlalchemy.ext.asyncio import AsyncSession
app = FastAPI()
# Async dependency
async def get_db() -> AsyncSession:
async with AsyncSessionLocal() as session:
yield session
# Async endpoints with types
@app.get("/users/{user_id}")
async def read_user(
user_id: int,
db: AsyncSession = Depends(get_db)
) -> User:
user = await db.get(User, user_id)
if user is None:
raise HTTPException(status_code=404, detail="User not found")
return user
@app.get("/users")
async def list_users(
skip: int = 0,
limit: int = 10,
db: AsyncSession = Depends(get_db)
) -> List[User]:
result = await db.execute(
select(User).offset(skip).limit(limit)
)
return result.scalars().all()
3. FastAPI の依存性注入の型
from typing import Annotated, Optional
from fastapi import FastAPI, Depends, Header, HTTPException
app = FastAPI()
# Typed dependencies
async def get_current_user(
authorization: Annotated[Optional[str], Header()] = None
) -> User:
if authorization is None:
raise HTTPException(status_code=401, detail="Not authenticated")
# Verify token and return user
return User(id=1, name="Current User", email="[email protected]")
# Use dependency with type annotation
@app.get("/me")
async def read_current_user(
current_user: Annotated[User, Depends(get_current_user)]
) -> User:
return current_user
# Complex dependency chain
class UserService:
def __init__(self, db: AsyncSession) -> None:
self.db = db
async def get_user(self, user_id: int) -> Optional[User]:
return await self.db.get(User, user_id)
def get_user_service(
db: Annotated[AsyncSession, Depends(get_db)]
) -> UserService:
return UserService(db)
@app.get("/users/{user_id}")
async def get_user_endpoint(
user_id: int,
service: Annotated[UserService, Depends(get_user_service)]
) -> User:
user = await service.get_user(user_id)
if user is None:
raise HTTPException(status_code=404, detail="User not found")
return user
Django との統合
1. django-stubs を使った Django
# Install django-stubs
pip install django-stubs mypy
# Generate mypy configuration
python -m mypy --install-types
# mypy.ini
[mypy]
plugins = mypy_django_plugin.main
[mypy.plugins.django-stubs]
django_settings_module = "myproject.settings"
2. 型ヒントを使った Django モデル
# models.py
from django.db import models
from typing import Optional
class User(models.Model):
email: models.EmailField = models.EmailField(unique=True)
name: models.CharField = models.CharField(max_length=100)
age: models.IntegerField = models.IntegerField(null=True, blank=True)
is_active: models.BooleanField = models.BooleanField(default=True)
created_at: models.DateTimeField = models.DateTimeField(auto_now_add=True)
def get_display_name(self) -> str:
return self.name or self.email
@classmethod
def get_active_users(cls) -> models.QuerySet["User"]:
return cls.objects.filter(is_active=True)
3. 型ヒントを使った Django ビュー
# views.py
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404
from typing import Dict, Any
from .models import User
def user_detail(request: HttpRequest, user_id: int) -> JsonResponse:
user: User = get_object_or_404(User, pk=user_id)
data: Dict[str, Any] = {
"id": user.id,
"name": user.name,
"email": user.email,
}
return JsonResponse(data)
def user_list(request: HttpRequest) -> JsonResponse:
users = User.get_active_users()
data = {
"users": list(users.values("id", "name", "email"))
}
return JsonResponse(data)
型スタブとサードパーティライブラリ
1. 型スタブのインストール
# Install stubs for popular libraries
pip install types-requests
pip install types-PyYAML
pip install types-redis
pip install types-boto3
# Search for available stubs
pip search types-
# Auto-install missing stubs
mypy --install-types
2. カスタムスタブの作成
# stubs/external_lib.pyi
from typing import Optional, List
class Client:
def __init__(self, api_key: str) -> None: ...
def get_user(self, user_id: int) -> Optional[dict]: ...
def list_users(self, limit: int = 10) -> List[dict]: ...
def connect(host: str, port: int) -> Client: ...
# mypy.ini
[mypy]
mypy_path = stubs
3. インポート不足を無視する
# mypy.ini
[mypy-external_lib.*]
ignore_missing_imports = True
# For multiple libraries
[mypy-celery.*,redis.*,boto3.*]
ignore_missing_imports = True
CI/CD との統合
1. GitHub Actions
# .github/workflows/type-check.yml
name: Type Check
on: [push, pull_request]
jobs:
mypy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install mypy
pip install -r requirements.txt
pip install types-requests types-PyYAML
- name: Run mypy
run: mypy src/
- name: Run mypy strict on new code
run: mypy --strict src/api/
2. pre-commit フック
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
args: [--strict, --ignore-missing-imports]
additional_dependencies:
- types-requests
- types-PyYAML
# Install pre-commit
pip install pre-commit
pre-commit install
# Run manually
pre-commit run mypy --all-files
3. Make ターゲット
# Makefile
.PHONY: typecheck
typecheck:
mypy src/
.PHONY: typecheck-strict
typecheck-strict:
mypy --strict src/
.PHONY: typecheck-report
typecheck-report:
mypy src/ --html-report mypy-report
@echo "Report: mypy-report/index.html"
.PHONY: ci
ci: typecheck test lint
よく使われるパターンと慣用表現
1. 複数のシグネチャに対するオーバーロード
from typing import overload, Union
@overload
def process(data: str) -> str: ...
@overload
def process(data: int) -> int: ...
@overload
def process(data: list) -> list: ...
def process(data: Union[str, int, list]) -> Union[str, int, list]:
"""Process different data types."""
if isinstance(data, str):
return data.upper()
elif isinstance(data, int):
return data * 2
else:
return [x * 2 for x in data]
# mypy knows return types
result1: str = process("hello") # Valid
result2: int = process(42) # Valid
result3: str = process(42) # Type error
2. 構造化された辞書に対する TypedDict
from typing import TypedDict, Optional
class UserDict(TypedDict):
id: int
name: str
email: str
age: Optional[int]
# Type-safe dict usage
def create_user(data: UserDict) -> UserDict:
return {
"id": 1,
"name": data["name"],
"email": data["email"],
"age": data.get("age"),
}
user: UserDict = {
"id": 1,
"name": "Alice",
"email": "[email protected]",
"age": 30
}
# Type error: Missing required key "email"
invalid_user: UserDict = {
"id": 1,
"name": "Bob",
}
3. Final と定数値
from typing import Final
# Constants that should never change
API_VERSION: Final = "v1"
MAX_RETRIES: Final[int] = 3
# Type error: Cannot assign to final name
API_VERSION = "v2"
# Final class (cannot be subclassed)
from typing import final
@final
class BaseConfig:
pass
# Type error: Cannot inherit from final class
class AppConfig(BaseConfig): # Error!
pass
4. メソッドチェーンのための Self 型
from typing import Self # Python 3.11+
class Builder:
def __init__(self) -> None:
self._value = 0
def add(self, value: int) -> Self:
self._value += value
return self
def multiply(self, value: int) -> Self:
self._value *= value
return self
def build(self) -> int:
return self._value
# Type-safe method chaining
result = Builder().add(5).multiply(2).add(3).build()
mypy と pyright の比較
機能の比較
| 機能 | mypy | pyright |
|---|---|---|
| 型チェッカー | Python の公式型チェッカー | Microsoft の型チェッカー |
| 速度 | 大規模なコードベースでは低速 | 高速、差分チェックに対応 |
| 厳格さ | 設定可能な厳格モード | デフォルトで非常に厳格 |
| IDE との統合 | 良好(LSP をサポート) | 優秀(VS Code の Pylance) |
| プラグインシステム | あり(mypy プラグイン) | 限定的 |
| エラーメッセージ | 明確で詳細 | 非常に詳細で役立つ |
| コミュニティ | 大規模、公式 | 拡大中、Microsoft の支援あり |
| 型推論 | 良好 | 優秀 |
| 設定 | mypy.ini、pyproject.toml | pyrightconfig.json、pyproject.toml |
mypy を使う場合
# Use mypy for:
# - Official Python type checking standard
# - Plugin ecosystem (Django, SQLAlchemy, Pydantic)
# - Gradual typing with fine-grained control
# - Compatibility with existing mypy configurations
# - CI/CD pipelines (industry standard)
pyright を使う場合
# Use pyright for:
# - VS Code development (Pylance)
# - Faster type checking on large codebases
# - Stricter type checking by default
# - Better type inference
# - Real-time IDE feedback
両方を使う場合
# pyproject.toml - Configure both
[tool.mypy]
strict = true
files = ["src"]
[tool.pyright]
include = ["src"]
strict = ["src/api"]
reportMissingTypeStubs = false
# Run both in CI
mypy src/
pyright src/
ローカル mypy プロファイル(ご自身のリポジトリ)
Python プロジェクトでよく使われるパターン:
- 厳格なデフォルト設定(edgar、kuzu-memory、mcp-browser):
disallow_untyped_defs = true、check_untyped_defs = true、no_implicit_optional = true、warn_return_any = true、strict_equality = true。 - 緩和プロファイル(mcp-ticketer):パッチリリース向けに、
disable_error_codeリストを使って厳格なフラグを一時的に無効にします。 - 段階的な導入(mcp-vector-search):型を安定させる間、
ignore_errors = trueを設定します。 - インポートの不足:mcp-memory と mcp-ticketer では
ignore_missing_imports = trueを使用します。
参考:edgar、kuzu-memory、mcp-vector-search、mcp-ticketer の pyproject.toml をご覧ください。
ベストプラクティス
1. 重要なモジュールから始める
# ✅ GOOD: Type critical business logic first
# services/user_service.py
from typing import Optional
class UserService:
def get_user(self, user_id: int) -> Optional[User]:
"""Retrieve user by ID."""
return self.db.query(User).get(user_id)
def create_user(self, data: UserCreate) -> User:
"""Create new user."""
user = User(**data.dict())
self.db.add(user)
self.db.commit()
return user
2. 読みやすさのために型エイリアスを使う
# ✅ GOOD: Clear, reusable type aliases
from typing import Dict, List, Union
JSON = Union[Dict[str, "JSON"], List["JSON"], str, int, float, bool, None]
Headers = Dict[str, str]
UserId = int
def parse_response(data: JSON, headers: Headers) -> UserId:
pass
# ❌ BAD: Complex inline types
def parse_response(
data: Union[Dict[str, Union[...]], List[...], str, int, float, bool, None],
headers: Dict[str, str]
) -> int:
pass
3. 暗黙的な指定より明示的な指定を優先する
# ✅ GOOD: Explicit types for public APIs
def get_user(user_id: int) -> Optional[User]:
return db.query(User).get(user_id)
# ❌ ACCEPTABLE: Type inference for internal helpers
def _format_name(first, last): # mypy infers str -> str
return f"{first} {last}"
4. デバッグには reveal_type を使う
# During development, check inferred types
from typing import reveal_type
def process_data(data):
result = transform(data)
reveal_type(result) # mypy: Revealed type is "int"
return result * 2
5. 型チェックの無視を文書化する
# ✅ GOOD: Document why type checking is disabled
import legacy_module # type: ignore[import] # TODO: Add type stubs
# ❌ BAD: No explanation
import legacy_module # type: ignore
よくある落とし穴
❌ アンチパターン 1:あらゆる場所で Any を使う
# WRONG: Defeats purpose of type checking
from typing import Any
def process(data: Any) -> Any:
return data.transform()
正しい例:
from typing import Union, Protocol
class Transformable(Protocol):
def transform(self) -> dict: ...
def process(data: Transformable) -> dict:
return data.transform()
❌ アンチパターン 2:型エラーを全体的に無視する
# WRONG: Disables type checking
[mypy]
ignore_errors = True
正しい例:
# Ignore specific modules only
[mypy-legacy.*]
ignore_errors = True
[mypy]
strict = True
❌ アンチパターン 3:Optional を使わない
# WRONG: Nullable without Optional
def get_user(user_id: int) -> User:
user = db.get(user_id) # Can be None!
return user # Runtime error if None
正しい例:
from typing import Optional
def get_user(user_id: int) -> Optional[User]:
return db.get(user_id)
# Handle None explicitly
user = get_user(123)
if user is not None:
print(user.name)
クイックリファレンス
よく使うコマンド
# Basic type checking
mypy main.py
mypy src/
# Strict mode
mypy --strict src/
# Install missing type stubs
mypy --install-types
# Generate HTML report
mypy src/ --html-report mypy-report
# Check specific error codes
mypy --show-error-codes src/
# Ignore missing imports
mypy --ignore-missing-imports src/
# Follow imports
mypy --follow-imports=silent src/
# Incremental mode (faster)
mypy --incremental src/
# Verbose output
mypy --verbose src/
エラーコードのリファレンス
# Common error codes
[attr-defined] # Attribute not defined
[arg-type] # Argument type mismatch
[return-value] # Return type mismatch
[assignment] # Assignment type mismatch
[call-overload] # No matching overload
[index] # Invalid index operation
[operator] # Unsupported operand type
[import] # Cannot find import
[misc] # Miscellaneous type error
[no-untyped-def] # Function missing type annotation
[var-annotated] # Variable needs type annotation
リソース
- 公式ドキュメント:https://mypy.readthedocs.io/
- 型ヒントの PEP:https://peps.python.org/pep-0484/
- typing モジュール:https://docs.python.org/3/library/typing.html
- mypy の GitHub:https://github.com/python/mypy
- 型スタブ:https://github.com/python/typeshed
- django-stubs:https://github.com/typeddjango/django-stubs
- FastAPI + mypy:https://fastapi.tiangolo.com/tutorial/type-hints/
関連スキル
mypy を使用する際は、次の補完的なスキルもご検討ください(スキルライブラリから利用できます)。
- pytest:mypy による型安全なテスト — テストスイートに型チェックを組み込み、包括的な型カバレッジを実現します
- fastapi-local-dev:完全な型安全性を備えた FastAPI — FastAPI の実行時検証と mypy の静的チェックを組み合わせます
- pydantic:mypy に対応した実行時の型検証 — mypy がコンパイル時に検証する一方で、実行時にデータを検証します
**mypy のバージョン互換性:**このスキルは mypy 1.8 以降を対象としており、2025 年時点の Python 型チェックのベストプラクティスを反映しています。
コメント
Markdown 対応