python-best-practices by 0xbigboss/claude-code
npx skills add https://github.com/0xbigboss/claude-code --skill python-best-practices类型在实现之前定义契约。遵循以下工作流程:
使用 Python 的类型系统在类型检查时防止无效状态。
用于结构化数据的数据类:
from dataclasses import dataclass
from datetime import datetime
@dataclass(frozen=True)
class User:
id: str
email: str
name: str
created_at: datetime
@dataclass(frozen=True)
class CreateUser:
email: str
name: str
# 冻结的数据类是不可变的 - 避免意外修改
使用 Literal 进行可区分联合:
from dataclasses import dataclass
from typing import Literal
@dataclass
class Idle:
status: Literal["idle"] = "idle"
@dataclass
class Loading:
status: Literal["loading"] = "loading"
@dataclass
class Success:
status: Literal["success"] = "success"
data: str
@dataclass
class Failure:
status: Literal["error"] = "error"
error: Exception
RequestState = Idle | Loading | Success | Failure
def handle_state(state: RequestState) -> None:
match state:
case Idle():
pass
case Loading():
show_spinner()
case Success(data=data):
render(data)
case Failure(error=err):
show_error(err)
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
用于领域原语的 NewType:
from typing import NewType
UserId = NewType("UserId", str)
OrderId = NewType("OrderId", str)
def get_user(user_id: UserId) -> User:
# 类型检查器阻止在此处传递 OrderId
...
def create_user_id(raw: str) -> UserId:
return UserId(raw)
用于约束值的枚举:
from enum import Enum, auto
class Role(Enum):
ADMIN = auto()
USER = auto()
GUEST = auto()
def check_permission(role: Role) -> bool:
match role:
case Role.ADMIN:
return True
case Role.USER:
return limited_check()
case Role.GUEST:
return False
# 如果缺少 case,类型检查器会发出警告
用于结构类型化的协议:
from typing import Protocol
class Readable(Protocol):
def read(self, n: int = -1) -> bytes: ...
def process_input(source: Readable) -> bytes:
# 接受任何具有 read() 方法的对象
return source.read()
用于外部数据形状的 TypedDict:
from typing import TypedDict, Required, NotRequired
class UserResponse(TypedDict):
id: Required[str]
email: Required[str]
name: Required[str]
avatar_url: NotRequired[str]
def parse_user(data: dict) -> UserResponse:
# 需要运行时验证 - TypedDict 是结构性的
return UserResponse(
id=data["id"],
email=data["email"],
name=data["name"],
)
倾向于更小、更专注的文件:每个模块一个类或一组紧密相关的函数。当一个文件处理多个关注点或超过约 300 行时进行拆分。使用 __init__.py 来公开公共 API;将实现细节保留在私有模块中(_internal.py)。将测试文件放在 tests/ 目录中,镜像源代码结构。
@dataclass(frozen=True);避免可变的默认参数。functools.partial 进行部分应用;组合小函数而不是使用大型类。from err 传播带有上下文的异常;捕获异常需要重新引发或返回有意义的结果。吞掉的异常会隐藏根本原因。None、边界值。在适当的情况下,在条件语句中包含 else 子句。pathlib 和显式编码。资源泄漏会导致生产问题。对未实现逻辑的显式失败:
def build_widget(widget_type: str) -> Widget:
raise NotImplementedError(f"build_widget not implemented for type: {widget_type}")
使用上下文传播以保留原始回溯:
try:
data = json.loads(raw)
except json.JSONDecodeError as err:
raise ValueError(f"invalid JSON payload: {err}") from err
带有显式默认值的穷举匹配:
def process_status(status: str) -> str:
match status:
case "active":
return "processing"
case "inactive":
return "skipped"
case _:
raise ValueError(f"unhandled status: {status}")
使用命名空间记录器的调试级别跟踪:
import logging
logger = logging.getLogger("myapp.widgets")
def create_widget(name: str) -> Widget:
logger.debug("creating widget: %s", name)
widget = Widget(name=name)
logger.debug("created widget id=%s", widget.id)
return widget
os.getenv 分散在整个代码中。使用数据类的类型化配置:
import os
from dataclasses import dataclass
@dataclass(frozen=True)
class Config:
port: int = 3000
database_url: str = ""
api_key: str = ""
env: str = "development"
@classmethod
def from_env(cls) -> "Config":
database_url = os.environ.get("DATABASE_URL", "")
if not database_url:
raise ValueError("DATABASE_URL is required")
return cls(
port=int(os.environ.get("PORT", "3000")),
database_url=database_url,
api_key=os.environ["API_KEY"], # 必需,如果缺失将引发异常
env=os.environ.get("ENV", "development"),
)
config = Config.from_env()
对于快速类型检查,可以考虑来自 Astral(ruff 和 uv 的创建者)的 ty。使用 Rust 编写,它比 mypy 或 pyright 快得多。
安装和使用:
# 使用 uvx 直接运行(无需安装)
uvx ty check
# 检查特定文件
uvx ty check src/main.py
# 永久安装
uv tool install ty
主要特性:
VIRTUAL_ENV 或 .venv)pyproject.toml 发现项目在 pyproject.toml 中的配置:
[tool.ty]
python-version = "3.12"
何时使用 ty 与替代方案:
ty - 最快,适用于 CI 和大型代码库(早期阶段,快速发展)pyright - 最完整的类型推断,VS Code 集成mypy - 成熟,广泛的插件生态系统每周安装次数
675
代码仓库
GitHub 星标数
37
首次出现
Jan 20, 2026
安全审计
安装于
opencode548
gemini-cli534
codex522
github-copilot488
claude-code446
cursor445
Types define the contract before implementation. Follow this workflow:
Use Python's type system to prevent invalid states at type-check time.
Dataclasses for structured data:
from dataclasses import dataclass
from datetime import datetime
@dataclass(frozen=True)
class User:
id: str
email: str
name: str
created_at: datetime
@dataclass(frozen=True)
class CreateUser:
email: str
name: str
# Frozen dataclasses are immutable - no accidental mutation
Discriminated unions with Literal:
from dataclasses import dataclass
from typing import Literal
@dataclass
class Idle:
status: Literal["idle"] = "idle"
@dataclass
class Loading:
status: Literal["loading"] = "loading"
@dataclass
class Success:
status: Literal["success"] = "success"
data: str
@dataclass
class Failure:
status: Literal["error"] = "error"
error: Exception
RequestState = Idle | Loading | Success | Failure
def handle_state(state: RequestState) -> None:
match state:
case Idle():
pass
case Loading():
show_spinner()
case Success(data=data):
render(data)
case Failure(error=err):
show_error(err)
NewType for domain primitives:
from typing import NewType
UserId = NewType("UserId", str)
OrderId = NewType("OrderId", str)
def get_user(user_id: UserId) -> User:
# Type checker prevents passing OrderId here
...
def create_user_id(raw: str) -> UserId:
return UserId(raw)
Enums for constrained values:
from enum import Enum, auto
class Role(Enum):
ADMIN = auto()
USER = auto()
GUEST = auto()
def check_permission(role: Role) -> bool:
match role:
case Role.ADMIN:
return True
case Role.USER:
return limited_check()
case Role.GUEST:
return False
# Type checker warns if case is missing
Protocol for structural typing:
from typing import Protocol
class Readable(Protocol):
def read(self, n: int = -1) -> bytes: ...
def process_input(source: Readable) -> bytes:
# Accepts any object with a read() method
return source.read()
TypedDict for external data shapes:
from typing import TypedDict, Required, NotRequired
class UserResponse(TypedDict):
id: Required[str]
email: Required[str]
name: Required[str]
avatar_url: NotRequired[str]
def parse_user(data: dict) -> UserResponse:
# Runtime validation needed - TypedDict is structural
return UserResponse(
id=data["id"],
email=data["email"],
name=data["name"],
)
Prefer smaller, focused files: one class or closely related set of functions per module. Split when a file handles multiple concerns or exceeds ~300 lines. Use __init__.py to expose public API; keep implementation details in private modules (_internal.py). Colocate tests in tests/ mirroring the source structure.
@dataclass(frozen=True) for immutable data; avoid mutable default arguments.functools.partial for partial application; compose small functions over large classes.from err; catching requires re-raising or returning a meaningful result. Swallowed exceptions hide root causes.None, boundary values. Include else clauses in conditionals where appropriate.pathlib and explicit encodings. Resource leaks cause production issues.Explicit failure for unimplemented logic:
def build_widget(widget_type: str) -> Widget:
raise NotImplementedError(f"build_widget not implemented for type: {widget_type}")
Propagate with context to preserve the original traceback:
try:
data = json.loads(raw)
except json.JSONDecodeError as err:
raise ValueError(f"invalid JSON payload: {err}") from err
Exhaustive match with explicit default:
def process_status(status: str) -> str:
match status:
case "active":
return "processing"
case "inactive":
return "skipped"
case _:
raise ValueError(f"unhandled status: {status}")
Debug-level tracing with namespaced logger:
import logging
logger = logging.getLogger("myapp.widgets")
def create_widget(name: str) -> Widget:
logger.debug("creating widget: %s", name)
widget = Widget(name=name)
logger.debug("created widget id=%s", widget.id)
return widget
os.getenv scattered throughout code.Typed config with dataclass:
import os
from dataclasses import dataclass
@dataclass(frozen=True)
class Config:
port: int = 3000
database_url: str = ""
api_key: str = ""
env: str = "development"
@classmethod
def from_env(cls) -> "Config":
database_url = os.environ.get("DATABASE_URL", "")
if not database_url:
raise ValueError("DATABASE_URL is required")
return cls(
port=int(os.environ.get("PORT", "3000")),
database_url=database_url,
api_key=os.environ["API_KEY"], # required, will raise if missing
env=os.environ.get("ENV", "development"),
)
config = Config.from_env()
For fast type checking, consider ty from Astral (creators of ruff and uv). Written in Rust, it's significantly faster than mypy or pyright.
Installation and usage:
# Run directly with uvx (no install needed)
uvx ty check
# Check specific files
uvx ty check src/main.py
# Install permanently
uv tool install ty
Key features:
VIRTUAL_ENV or .venv)pyproject.tomlConfiguration inpyproject.toml:
[tool.ty]
python-version = "3.12"
When to use ty vs alternatives:
ty - fastest, good for CI and large codebases (early stage, rapidly evolving)pyright - most complete type inference, VS Code integrationmypy - mature, extensive plugin ecosystemWeekly Installs
675
Repository
GitHub Stars
37
First Seen
Jan 20, 2026
Security Audits
Gen Agent Trust HubWarnSocketPassSnykPass
Installed on
opencode548
gemini-cli534
codex522
github-copilot488
claude-code446
cursor445
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
102,200 周安装
Gemini Interactions API 指南:统一接口、智能体交互与服务器端状态管理
833 周安装
Apollo MCP 服务器:让AI代理通过GraphQL API交互的完整指南
834 周安装
智能体记忆系统构建指南:分块策略、向量存储与检索优化
835 周安装
Scrapling官方网络爬虫框架 - 自适应解析、绕过Cloudflare、Python爬虫库
836 周安装
抽奖赢家选取器 - 随机选择工具,支持CSV、Excel、Google Sheets,公平透明
838 周安装
Medusa 前端开发指南:使用 SDK、React Query 构建电商商店
839 周安装