person-share/python_rules_3_12.md

12 KiB
Raw Blame History

Python 规范

依赖管理

  • 使用 uv 管理依赖
    uv init --python 3.12 .
    uv add <package_name>
    ...
    uv sync
    uv lock # 锁定确切版本
    
  • 明确区分生产依赖和开发依赖
    • 生产依赖:uv add <package_name>
    • 开发依赖(测试、构建、静态检查等):uv add --dev <package_name>
  • 明确版本约束
    • pyproject.toml 中使用语义化版本约束,避免因依赖库大版本更新(如 Pydantic V1 -> V2导致的不兼容问题。
    • 示例:pydantic>=2.0,<3.0
  • 补充类型存根
    • 对于不自带类型定义的第三方库,必须添加对应的 types-* 包以支持静态类型检查 。
    • 示例:uv add types-requests types-pytz

代码规则

  • 显式声明类型
    • 方法参数、属性、返回值等必须显式声明类型。
    • 禁止使用 Dict 传递非结构化的参数、返回值或数据对象,应使用 dataclassPydantic 模型。
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 自动格式化和检查。
    • 最大行宽不超过 88Black 默认)或团队约定值。
  • 命名规范

    • 模块、变量、函数: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)
    • 价值:标准化日志字段(timestampleveleventtrace_id…),与 ELK/Loki/ClickHouse 等分析平台无缝集成,提升故障定位与监控能力。
    • 建议:从项目骨架开始引入,定义公共 Logger 配置,并在 CI 或部署文档中说 明如何在生产环境启动 JSON 输出。

安全规范

  • 输入验证与净化
    • 所有外部输入API 请求、配置文件、用户上传内容等)都必须通过 Pydantic 模型进 行严格的验证和类型转换。
  • 敏感数据处理
    • 禁止在日志中明文记录密码、API Key 等敏感信息。
    • 使用 pydantic-settings 等工具从环境变量或安全的 Secret 管理服务中加载敏感 配置。
  • 依赖库安全性检查
    • 定期使用 uv auditpip-audit 扫描项目依赖,及时发现并修复已知的安全漏 洞。
    • 在 CI 流程中集成依赖扫描步骤。

测试最佳实践

  • 测试行为而非实现

    • 单元测试应验证公共接口的业务逻辑和行为,而不是其内部实现细节。
  • 隔离测试单元

    • 必须 Mock 所有外部依赖(如 API 调用、数据库、文件系统),确保测试的独立性和 稳定性。
  • 集成测试策略

    • 编写集成测试来验证关键模块间的交互,例如服务与数据库的连接。
    • 使用测试容器Testcontainers或 Docker Compose 为集成测试提供隔离的、真实的 依赖服务。
  • 性能测试

    • 对性能敏感的关键路径,使用 pytest-benchmark 等工具编写基准测试,防止性能回 退。
  • 测试数据管理

    • 使用 Faker 等库生成隔离且可复现的测试数据。
    • 严禁在测试中使用生产环境数据。
  • 处理无头环境中的 GUI 依赖

    • 在无头HeadlessCI/CD 环境中,若项目依赖 GUI 库(如 pyautogui),会导致 DISPLAY 环境变量缺失错误。

    • 解决方案:在 tests/conftest.py 中提前 Mock 相关的 GUI 模块。

      # 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. 利用 elsefinally

    • else:用于“正常流程”代码。
    • finally:用于资源清理,确保总能执行。
  5. 让意外异常继续冒泡
    只处理预期内的异常,不隐藏未知错误。

  6. 结合上下文管理器
    优先使用 with 语句管理资源,自动处理异常和清理。

性能优化指南

  • 内存使用优化
    • 优先使用生成器(yield)处理大型数据集,避免一次性加载到内存。
    • 对固定且属性较多的对象使用 __slots__ 来减少内存占用。
  • 并发/异步编程
    • 对 I/O 密集型任务,优先使用 asyncioasync/await 提高并发能力。
    • 避免在异步代码中调用阻塞的 I/O 操作。
  • 大数据处理
    • 在处理大型表格数据时,考虑使用 Polars 等高性能库。
    • 数据处理应以流式Streaming或分块Chunking方式进行。

静态类型检查

  • 使用 Mypy(或 Pyright)在 CI 中强制执行类型检查。

    mypy .
    
  • 推荐启用 --strict 模式,或根据团队情况定制 pyproject.toml 中的 [tool.mypy] 配置。

  • 全面开启 Ruff

    • 价值:在 PEP 8 之外再覆盖现代化升级提示UP*)、常见 BugB/BLE、安 全隐患S等规则一次性收割多类质量红利。
    • 建议:在 pyproject.toml 中启用最全的 select 集合CI 里把 Ruff 报错 作为门禁。

项目文档与知识沉淀

  • 记录已知“坑点”

    • 在项目 CLAUDE.mdREADME.md 中,必须建立一个专门区域,记录开发过程中遇 到的特有问题、环境陷阱和解决方案。
    • 示例记录

      项目特有的异常行为或警告

      1. GUI 依赖问题: 在无头环境测试需要 Mock pyautogui
      2. 时区格式化: pytz 库对北京时区的缩写可能返回 LMT 而非 CST,测 试时需灵活判断。
  • 提供环境配置文档

    • 为新成员准备清晰的环境配置指南,包括必要的环境变量和工具链。

环境与工作流

  • 在项目根目录提供 Makefilepyproject.toml [project.scripts] 封装常用命令 。
# 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数据类从根本上禁用动态增 删属性。
    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 中 针对性豁免;同时在代码审查中重点关注。
      [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此时重载能让代码更自然、更符合领域习惯。
    • 禁止场景:避免在通用业务对象上重载运算符,这会降低代码的清晰度和可维护性 。
    # ✅ 推荐:在数学/几何领域,重载使代码更直观
    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,保证子进程日志和耗时信息完整输出。