关于 mypy
mypy 是 Python 静态类型检查工具,适用于希望在保留动态语言特性的同时逐步采用类型标注的开发者。它支持严格检查、类型推断、插件及增量检查。
主要功能
- 01逐步采用类型标注
- 02严格模式检查
- 03类型推断与结构化类型
- 04泛型和高级类型模式
- 05插件扩展检查
- 06大型代码库增量检查
适用场景
- 01在运行前发现类型错误
- 02逐步为既有 Python 项目补充类型
- 03为 FastAPI 或 Django 项目执行类型检查
- 04在 CI 或提交钩子中运行检查
安装
复制下面的命令,在终端中运行。
$
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 标志获得最高级别的类型安全
- 类型推断:根据上下文自动推断类型
- Protocol 支持:结构化类型(带类型的鸭子类型)
- 泛型类型: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. Literal 类型
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 Hook
# .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. 使用 Overload 支持多个签名
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