## Python 规范 ### 依赖管理 - **使用 uv 管理依赖** ```bash uv init --python 3.12 . uv add ... uv sync uv lock # 锁定确切版本 ``` - **明确区分生产依赖和开发依赖** - 生产依赖:`uv add ` - 开发依赖(测试、构建、静态检查等):`uv add --dev ` - **明确版本约束** - 在 `pyproject.toml` 中使用语义化版本约束,避免因依赖库大版本更新(如 Pydantic V1 -> V2)导致的不兼容问题。 - 示例:`pydantic>=2.0,<3.0` - **补充类型存根** - 对于不自带类型定义的第三方库,必须添加对应的 `types-*` 包以支持静态类型检查 。 - 示例:`uv add types-requests types-pytz` ### 代码规则 - **显式声明类型** - 方法参数、属性、返回值等必须显式声明类型。 - 禁止使用 `Dict` 传递非结构化的参数、返回值或数据对象,应使用 `dataclass` 或 `Pydantic` 模型。 ```python from pydantic import BaseModel, field_validator class Config(BaseModel): min_poll_interval: int = 5 max_poll_interval: int = 1800 @field_validator('max_poll_interval') @classmethod def validate_max_greater_than_min(cls, v, info): if 'min_poll_interval' in info.data and v <= info.data['min_poll_interval']: raise ValueError('max_poll_interval must be greater than min_poll_interval') return v ``` - **用绝对导入** - 使用绝对导入,而不是相对导入。 - 绝对导入的优点是:更清晰,更容易理解。 - **运行时数据验证** - 充分利用 Pydantic 的验证器(`@field_validator`)对配置、API 输入等进行严格的 运行时检查,提前防止数据错误。 - **构造函数初始化** - 实例必须的初始化工作应当在 `__init__` 构造函数里完成,而不是让调用者额外调用 “init” 方法。 - **遵循 PEP 8** - 使用 Black + Ruff/isort 自动格式化和检查。 - 最大行宽不超过 88(Black 默认)或团队约定值。 - **命名规范** - 模块、变量、函数:`snake_case` - 类名、异常名:`PascalCase` - 常量:`UPPER_SNAKE_CASE` - 内部方法/函数 名字需要以 _ 开头 - **文档与注释** - 每个模块、类、函数都要有 docstring,遵循 Google 样式。 - 在复杂逻辑处添加必要注释,解释“为什么”这么做,而不是“做了什么”。 ### 接口与抽象 - **合理使用 `typing.Protocol`** - **价值**:在策略和插件化场景下,用协议定义契约比显式继承更灵活,既保持了低耦 合,又兼顾了 IDE 智能补全和 Mypy 校验。 - **建议**:对外暴露的接口优先使用 `Protocol`,内部默认实现再继承或注册。 ### 配置管理 - **引入 `pydantic-settings` 统一配置** - **价值**:把 `.env`、环境变量、CLI 参数统一为 Pydantic 模型,避免项目里到处 零散地调用 `os.getenv`,提高了配置的可追溯性和类型安全。 - **建议**:新建一个 `Settings` 类集中管理,配合 `uv add pydantic-settings` 并 在 `uv.sync` 后自动生成对应代码片段。 ### 日志管理 - **结构化日志 (structlog + JSON sink)** - **价值**:标准化日志字段(`timestamp`、`level`、`event`、`trace_id`…),与 ELK/Loki/ClickHouse 等分析平台无缝集成,提升故障定位与监控能力。 - **建议**:从项目骨架开始引入,定义公共 `Logger` 配置,并在 CI 或部署文档中说 明如何在生产环境启动 JSON 输出。 ### 安全规范 - **输入验证与净化** - 所有外部输入(API 请求、配置文件、用户上传内容等)都必须通过 Pydantic 模型进 行严格的验证和类型转换。 - **敏感数据处理** - 禁止在日志中明文记录密码、API Key 等敏感信息。 - 使用 `pydantic-settings` 等工具从环境变量或安全的 Secret 管理服务中加载敏感 配置。 - **依赖库安全性检查** - 定期使用 `uv audit` 或 `pip-audit` 扫描项目依赖,及时发现并修复已知的安全漏 洞。 - 在 CI 流程中集成依赖扫描步骤。 ### 测试最佳实践 - **测试行为而非实现** - 单元测试应验证公共接口的业务逻辑和行为,而不是其内部实现细节。 - **隔离测试单元** - 必须 Mock 所有外部依赖(如 API 调用、数据库、文件系统),确保测试的独立性和 稳定性。 - **集成测试策略** - 编写集成测试来验证关键模块间的交互,例如服务与数据库的连接。 - 使用测试容器(Testcontainers)或 Docker Compose 为集成测试提供隔离的、真实的 依赖服务。 - **性能测试** - 对性能敏感的关键路径,使用 `pytest-benchmark` 等工具编写基准测试,防止性能回 退。 - **测试数据管理** - 使用 `Faker` 等库生成隔离且可复现的测试数据。 - 严禁在测试中使用生产环境数据。 - **处理无头环境中的 GUI 依赖** - 在无头(Headless)CI/CD 环境中,若项目依赖 GUI 库(如 `pyautogui`),会导致 `DISPLAY` 环境变量缺失错误。 - **解决方案**:在 `tests/conftest.py` 中提前 Mock 相关的 GUI 模块。 ```python # tests/conftest.py import sys from unittest.mock import MagicMock # 在导入测试用例前Mock GUI相关模块 sys.modules['pyautogui'] = MagicMock() sys.modules['pygetwindow'] = MagicMock() ``` - **编写健壮的测试** - 测试用例应具备健壮性,不应依赖具体的、可能变化的实现细节(如时区缩写 "CST" vs "LMT")。应测试其核心逻辑。 - 使用参数化测试(`@pytest.mark.parametrize`)覆盖边界条件和异常情况。 ### 异常处理最佳实践 1. **捕获具体异常** 避免使用 `except Exception:` 或裸 `except:`,应捕获特定异常。 2. **记录日志而非打印** 在 `except` 块中使用 `logging` 模块记录异常信息(包括堆栈跟踪)。 3. **定义自定义异常类** 针对业务领域定义专用异常类,使错误类型更加语义化。 4. **利用 `else` 与 `finally`** - `else`:用于“正常流程”代码。 - `finally`:用于资源清理,确保总能执行。 5. **让意外异常继续冒泡** 只处理预期内的异常,不隐藏未知错误。 6. **结合上下文管理器** 优先使用 `with` 语句管理资源,自动处理异常和清理。 ### 性能优化指南 - **内存使用优化** - 优先使用生成器(`yield`)处理大型数据集,避免一次性加载到内存。 - 对固定且属性较多的对象使用 `__slots__` 来减少内存占用。 - **并发/异步编程** - 对 I/O 密集型任务,优先使用 `asyncio` 和 `async/await` 提高并发能力。 - 避免在异步代码中调用阻塞的 I/O 操作。 - **大数据处理** - 在处理大型表格数据时,考虑使用 `Polars` 等高性能库。 - 数据处理应以流式(Streaming)或分块(Chunking)方式进行。 ### 静态类型检查 - 使用 **Mypy**(或 **Pyright**)在 CI 中强制执行类型检查。 ```bash mypy . ``` - 推荐启用 `--strict` 模式,或根据团队情况定制 `pyproject.toml` 中的 `[tool.mypy]` 配置。 - **全面开启 Ruff** - **价值**:在 PEP 8 之外,再覆盖现代化升级提示(UP\*)、常见 Bug(B/BLE)、安 全隐患(S)等规则,一次性收割多类质量红利。 - **建议**:在 `pyproject.toml` 中启用最全的 `select` 集合,CI 里把 Ruff 报错 作为门禁。 ### 项目文档与知识沉淀 - **记录已知“坑点”** - 在项目 `CLAUDE.md` 或 `README.md` 中,必须建立一个专门区域,记录开发过程中遇 到的特有问题、环境陷阱和解决方案。 - **示例记录**: > #### 项目特有的异常行为或警告 > > 1. **GUI 依赖问题**: 在无头环境测试需要 Mock `pyautogui`。 > 2. **时区格式化**: `pytz` 库对北京时区的缩写可能返回 `LMT` 而非 `CST`,测 > 试时需灵活判断。 - **提供环境配置文档** - 为新成员准备清晰的环境配置指南,包括必要的环境变量和工具链。 ### 环境与工作流 - 在项目根目录提供 `Makefile` 或 `pyproject.toml [project.scripts]` 封装常用命令 。 ```toml # pyproject.toml [project.scripts] lint = "ruff check . --fix" format = "black ." test = "pytest --cov=src" check = "mypy ." ``` - **使用 pre-commit Hooks** - 强烈建议配置 pre-commit,在代码提交前自动执行格式化和静态检查,保证代码质量 ### 团队协作与发布流程 - **代码审查清单** - 在项目中创建 `PULL_REQUEST_TEMPLATE.md`,提供一个清单,确保审查点(如测试覆 盖、文档更新、遵循规范)不被遗漏。 - **版本管理策略** - 遵循语义化版本(SemVer)规范(主版本.次版本.修订号)。 - 使用 Git 标签(`git tag`)标记每个正式发布版本。 - **发布流程** - 推荐使用 CI/CD 流水线自动化测试、构建和发布流程。 ### 语言限制 - **禁止在运行时变更对象布局** - 所有类必须声明 `__slots__` 或使用冻结(immutable)数据类,从根本上禁用动态增 删属性。 ```python from dataclasses import dataclass @dataclass(frozen=True, slots=True) class Point: x: float y: float # 使用 frozen=True 和 slots=True 同时获得不可变性和性能优化 # 任何修改属性或添加新属性的操作都会抛出异常 ``` - **「禁止 Any」与「全员 frozen + slots」** - **价值**:极大提升类型检查严格度和运行时内存/访问性能,并防止动态增删属性引 发的隐蔽 Bug。 - **建议**:主业务代码强制执行,测试代码,实验性或 PoC 模块可在 `mypy.ini` 中 针对性豁免;同时在代码审查中重点关注。 ```toml [mypy] disallow_untyped_defs = True # 所有函数/方法必须有类型注释 disallow_any_unimported = True # 禁止隐式 Any(未导入类型) disallow_any_explicit = True # 禁止显式 Any warn_unused_ignores = True no_implicit_optional = True [mypy-poc.*] # 实验性或 PoC 模块 disallow_untyped_defs = False disallow_any_explicit = False [mypy-tests.*] disallow_untyped_defs = False disallow_any_explicit = False ``` - **谨慎使用运算符重载** - **原则**:仅在能显著提高代码可读性和表达力时才使用运算符重载,避免滥用。 - **适用场景**:主要用于数学计算(如向量、矩阵)、数据分析或构建领域特定语言( DSL)等,此时重载能让代码更自然、更符合领域习惯。 - **禁止场景**:避免在通用业务对象上重载运算符,这会降低代码的清晰度和可维护性 。 ```python # ✅ 推荐:在数学/几何领域,重载使代码更直观 class Vector: def __init__(self, x, y): self.x, self.y = x, y def __add__(self, other): return Vector(self.x + other.x, self.y + other.y) # ❌ 不推荐:在业务对象上重载,含义模糊 class ShoppingCart: def __add__(self, item): ... # 含义不清晰,应使用 .add_item(item) ``` ### 信号处理与优雅关闭 - 使用 `signal.signal()` 注册 `SIGTERM`/`SIGINT` 处理函数,收到信号后停止接收新任务,优雅退出。 - 在 `try...finally` 中确保调用子进程或线程的 `join()`/`close()`,避免僵尸进程或资源泄漏。 ### 测试与调试 - `pytest` 命令加上 `-s --durations=0 --log-cli-level=INFO`,保证子进程日志和耗时信息完整输出。