From python
Python类型注解与类型安全规范。涵盖PEP 695泛型语法、Pydantic v2模型验证、mypy严格模式配置。适用于添加类型注解、类型检查报错修复、数据模型定义、泛型设计等场景。
npx claudepluginhub lazygophers/ccplugin --plugin pythonThis skill uses the workspace's default tool permissions.
- **python:dev** - 开发阶段使用
Provides Ktor server patterns for routing DSL, plugins (auth, CORS, serialization), Koin DI, WebSockets, services, and testApplication testing.
Conducts multi-source web research with firecrawl and exa MCPs: searches, scrapes pages, synthesizes cited reports. For deep dives, competitive analysis, tech evaluations, or due diligence.
Provides demand forecasting, safety stock optimization, replenishment planning, and promotional lift estimation for multi-location retailers managing 300-800 SKUs.
所有公共 API 必须包含类型注解:
# ✅ 正确:完整类型注解
def fetch_user(user_id: int) -> User | None:
"""获取用户(Python 3.10+ union 语法)"""
return database.get(user_id)
# ❌ 错误:缺少类型注解
def fetch_user(user_id):
return database.get(user_id)
使用新的泛型语法:
# ✅ 新语法(Python 3.12+)
def first[T](items: list[T]) -> T | None:
"""获取列表第一个元素(泛型函数)"""
return items[0] if items else None
class Container[T]:
"""泛型容器(类型参数)"""
def __init__(self, value: T) -> None:
self.value = value
def get(self) -> T:
return self.value
# ⚠️ 旧语法(Python 3.11-)- 向后兼容
from typing import TypeVar, Generic
T = TypeVar('T')
class OldContainer(Generic[T]):
def __init__(self, value: T) -> None:
self.value = value
核心改进:
model_config)from pydantic import BaseModel, Field, ConfigDict, field_validator, model_validator
from typing import Annotated, Self
class User(BaseModel):
"""用户模型(Pydantic v2 风格)"""
model_config = ConfigDict(
str_strip_whitespace=True, # 自动去除空格
validate_assignment=True, # 赋值时验证
frozen=False, # 是否不可变
)
id: int
username: Annotated[str, Field(min_length=3, max_length=50)]
email: EmailStr
age: Annotated[int, Field(ge=0, le=150)]
# 字段验证器(v2 语法)
@field_validator('username')
@classmethod
def validate_username(cls, v: str) -> str:
if not v.isalnum():
raise ValueError('username must be alphanumeric')
return v.lower()
# 模型验证器(v2 语法)
@model_validator(mode='after')
def check_age_consistency(self) -> Self:
if self.age < 18 and not self.email.endswith('@school.edu'):
raise ValueError('minors must use school email')
return self
v1 → v2 迁移要点:
Config → model_config(ConfigDict)@validator → @field_validator / @model_validator.dict() → .model_dump().json() → .model_dump_json()# ✅ 新语法(Python 3.12+)
type UserId = int
type UserName = Annotated[str, Field(min_length=3, max_length=50)]
type UserDict = dict[str, User]
def get_user(user_id: UserId) -> User:
...
# ⚠️ 旧语法(向后兼容)
UserId = int
UserName = str
UserDict = Dict[str, User]
优先使用 Protocol 而非 ABC:
from typing import Protocol
class Drawable(Protocol):
"""可绘制对象协议"""
def draw(self) -> None: ...
def get_bounds(self) -> tuple[int, int, int, int]: ...
class Circle:
def draw(self) -> None:
print("Drawing circle")
def get_bounds(self) -> tuple[int, int, int, int]:
return (0, 0, 100, 100)
def render(obj: Drawable) -> None:
"""渲染可绘制对象"""
obj.draw() # 任何有 draw() 方法的对象都可以
# Circle 自动符合 Drawable 协议,无需继承
render(Circle())
from typing import TypedDict, NotRequired
class UserDict(TypedDict):
"""用户字典类型"""
id: int
username: str
email: str
age: NotRequired[int] # 可选字段(Python 3.11+)
def create_user(data: UserDict) -> User:
return User(**data)
# ✅ 正确
user_data: UserDict = {
"id": 1,
"username": "alice",
"email": "alice@example.com"
}
# ❌ 错误:mypy 会报错
invalid_data: UserDict = {
"id": "not_an_int", # 类型错误
"username": "alice"
}
from typing import Literal
from enum import Enum
# Literal 用于固定值
Status = Literal["pending", "running", "completed", "failed"]
def get_task_status() -> Status:
return "completed" # ✅ 只能返回这 4 个值之一
# Enum 用于复杂枚举
class TaskStatus(str, Enum):
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
def update_status(status: TaskStatus) -> None:
print(f"Status: {status.value}")
update_status(TaskStatus.COMPLETED)
| AI 可能的理性化解释 | 实际应该检查的内容 |
|---|---|
| "简单函数不需要类型" | ✅ 是否所有公共函数都有类型注解? |
| "Any 类型足够灵活" | ✅ 是否滥用 Any(应该 < 5%)? |
| "运行时类型检查更安全" | ✅ 是否优先使用静态类型检查(mypy)? |
| "Pydantic v1 够用了" | ✅ 是否迁移到 Pydantic v2(性能提升 5-50 倍)? |
| "旧的泛型语法更兼容" | ✅ 是否使用 Python 3.12+ PEP 695 语法? |
| "TypedDict 太复杂" | ✅ 字典参数是否使用 TypedDict? |
[tool.mypy]
python_version = "3.13"
strict = true
# 严格检查
warn_return_any = true
warn_unused_ignores = true
disallow_untyped_defs = true
disallow_any_generics = true
check_untyped_defs = true
# 第三方库类型
ignore_missing_imports = false
# 性能优化
cache_dir = ".mypy_cache"
incremental = true
# 每个模块单独配置
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false # 测试文件可以放宽
# 严格模式检查
mypy --strict src/
# 生成覆盖率报告
mypy --html-report mypy-report src/
# 只检查特定文件
mypy src/models.py src/api.py
[tool.ruff.lint]
select = [
"ANN", # 强制类型注解
]
ignore = [
"ANN101", # missing-type-self(不强制 self 注解)
"ANN102", # missing-type-cls(不强制 cls 注解)
]
[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["ANN"] # 测试文件不强制类型注解
from typing import Protocol
class Entity(Protocol):
"""实体协议"""
id: int
class Repository[T: Entity]:
"""泛型仓储(Python 3.12+ 语法)"""
def __init__(self) -> None:
self._data: dict[int, T] = {}
def find(self, id: int) -> T | None:
"""查找实体"""
return self._data.get(id)
def save(self, entity: T) -> None:
"""保存实体"""
self._data[entity.id] = entity
def find_all(self) -> list[T]:
"""查找所有实体"""
return list(self._data.values())
# 使用
class User:
def __init__(self, id: int, name: str) -> None:
self.id = id
self.name = name
user_repo = Repository[User]()
user_repo.save(User(1, "Alice"))
user = user_repo.find(1)
from pydantic import BaseModel, Field, computed_field, field_validator
from typing import Annotated
class Address(BaseModel):
"""地址模型"""
street: str
city: str
country: str = "USA"
class User(BaseModel):
"""用户模型(展示所有 Pydantic v2 特性)"""
model_config = ConfigDict(
str_strip_whitespace=True,
validate_assignment=True,
)
id: int
username: Annotated[str, Field(min_length=3, max_length=50)]
email: EmailStr
age: Annotated[int, Field(ge=0, le=150)]
address: Address | None = None
@computed_field
@property
def display_name(self) -> str:
"""计算字段"""
return f"{self.username} ({self.email})"
@field_validator('username')
@classmethod
def validate_username(cls, v: str) -> str:
if not v.isalnum():
raise ValueError('username must be alphanumeric')
return v.lower()
# 使用
user = User(
id=1,
username="Alice",
email="alice@example.com",
age=25,
address=Address(street="123 Main St", city="NYC")
)
print(user.model_dump()) # v2: 导出字典
print(user.model_dump_json()) # v2: 导出 JSON
| 而非 Union)Annotated 添加元数据约束model_config 替代 Config@field_validator 替代 @validator.model_dump() 替代 .dict()mypy --strict 无错误# type: ignore 注释(除非必要)Any 类型使用 < 5%