python-coding-standards

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

python-coding-standards

Python编码规范

Defaults for writing and editing Python, so every file in a project reads consistently regardless of which session wrote it. None of this applies to a snippet written purely to illustrate a concept, with no runnable entry point, that nobody is going to execute — e.g. a one-off example in prose showing what a decorator looks like. If you can't tell whether something has a runnable entry point, assume it does. Everything else — including a demo script, a one-off, or an example saved to a file — is real code the rules below apply to.
本规范为Python代码编写与编辑的默认标准,确保项目中所有文件无论由谁编写,风格都保持一致。本规范不适用于仅用于演示概念、无可运行入口、不会被执行的代码片段——例如用于展示装饰器写法的示例代码。若无法判断代码是否有可运行入口,请默认其具备可运行入口。除此之外的所有代码——包括演示脚本、一次性脚本或保存到文件的示例——均属于正式代码,需遵循以下规则。

When to Invoke

适用场景

  • About to create or edit a
    .py
    file, or write Python code the user is going to run — except code covered by the illustrative-example exemption above.
  • 即将创建或编辑
    .py
    文件,或是编写用户将运行的Python代码——除上述概念演示类代码外。

Core Rules

核心规则

  1. [NEVER VIOLATE] Functions the user is going to run get complete type hints. Every parameter and the return type — whether the code ends up in a file or stays in the chat as something to copy-paste and execute; a one-off script is still real code. When editing an existing file, this applies to the function you're adding or touching — don't backfill type hints across the rest of the file unless asked.
  2. [NEVER VIOLATE] No global variables. Use instance attributes, closures, or dependency injection instead. The smell this rule targets is a module-level or class-level name that gets mutated after it's defined — whether one function touches it (the
    global
    keyword is the textbook case) or several. A constant — module-level or class-level, scalar or a dict/list/tuple that's never reassigned or mutated after definition (
    MAX_RETRIES = 3
    ,
    DEFAULT_HEADERS = {...}
    ) — is not a global variable and is fine, and so is a module-level
    logger = logging.getLogger(__name__)
    .
  3. Use
    logging
    , never
    print()
    , for anything diagnostic.
    print()
    is acceptable only for a CLI tool's actual user-facing output — the thing the program exists to print, not a debug trace left behind. A standalone script calls
    logging.basicConfig(level=logging.INFO, format=...)
    once at its entry point (
    if __name__ == "__main__":
    ) — without an explicit
    level=
    , the root logger defaults to WARNING and
    logger.info
    silently produces no output — a module meant to be imported never calls
    basicConfig
    itself, that's the importer's call.
  4. Wrap in try/except, and log the failure, any I/O that can fail for reasons your own code doesn't control: HTTP calls, DB connections, subprocess calls, and file reads/writes. This doesn't cover writing to stdout/stderr (Rule 3's
    print()
    case) — that doesn't fail for external reasons. Log enough to diagnose without a repro — what was called, and the actual exception, not just "failed".
  5. Comment the WHY, not the WHAT. Only add a comment when it explains something the code itself can't — a hidden constraint, a non-obvious trade-off, a workaround for a specific bug. A well-named function or variable already says what it does; restating that in a comment is noise that goes stale the moment the code changes.
  6. Tooling:
    .venv
    +
    requirements.txt
    , PEP 8, Black.
  1. 【绝对禁止违反】用户将运行的函数必须包含完整的type hints。每个参数和返回值都需要添加类型提示——无论代码最终保存到文件还是留在聊天框供用户复制执行;一次性脚本也属于正式代码。编辑现有文件时,仅需为新增或修改的函数添加类型提示,无需为文件中其他函数补全类型提示,除非用户明确要求。
  2. 【绝对禁止违反】禁止使用全局变量。请使用实例属性、闭包或依赖注入替代。本规则针对的是模块级或类级别的变量被定义后被修改的情况——无论是单个函数修改(使用
    global
    关键字是典型案例)还是多个函数修改。常量——模块级或类级别的、定义后从未被重新赋值或修改的标量、字典、列表或元组(例如
    MAX_RETRIES = 3
    DEFAULT_HEADERS = {...}
    )——不属于全局变量,是允许使用的;模块级别的
    logger = logging.getLogger(__name__)
    也同样允许。
  3. 所有诊断信息请使用
    logging
    ,绝对禁止使用
    print()
    。仅当
    print()
    用于CLI工具面向用户的实际输出时才允许使用——即程序存在的目的就是输出该内容,而非遗留的调试痕迹。独立脚本需在入口点(
    if __name__ == "__main__":
    )处调用一次
    logging.basicConfig(level=logging.INFO, format=...)
    ——若未显式指定
    level=
    ,根日志器默认级别为WARNING,
    logger.info
    将不会产生任何输出;供导入的模块本身绝不能调用
    basicConfig
    ,该操作应由导入方执行。
  4. 所有因外部因素可能失败的I/O操作,需包裹在try/except块中并记录失败信息:包括HTTP请求、数据库连接、子进程调用以及文件读写。本规则不适用于向stdout/stderr写入内容(即规则3中允许的
    print()
    场景)——此类操作不会因外部因素失败。记录的信息需足够用于排查问题,无需重现场景——需记录调用内容以及实际异常信息,而非仅记录“失败”。
  5. 注释需解释“原因”,而非“内容”。仅当代码无法自行解释时才添加注释——例如隐藏的约束、非显而易见的权衡、针对特定Bug的规避方案。命名良好的函数或变量已经能说明其功能,重复内容的注释属于冗余信息,且代码变更后注释会失效。
  6. 工具链:
    .venv
    +
    requirements.txt
    、PEP 8、Black

Testing

测试要求

  • Every piece of logic you write or change — a new script, a new feature, or a bug fix — needs a test that exercises its business logic — the behaviour a caller depends on, not the fact that the function ran. [NEVER VIOLATE] Never hardcode a return value, or assert a tautology, just to make a test pass — this applies to a probe just as much as a formal test.
  • Test infra (e.g.
    pytest
    ) already exists → write the test first, then the implementation.
  • No test infra, but the code lives in an existing project (a repo, a package — somewhere with a future) → set up
    pytest
    rather than inventing a bespoke runner. It's the de facto standard, so any future session or agent already knows how to run it.
  • No test infra, and the code really is a standalone one-off with no project to land in → at minimum, write a runnable probe: a short standalone snippet that feeds the new code a known input and checks the actual output against what you expect, not just that it ran without crashing. The probe satisfies this rule, it isn't an exemption from it.
  • Project already has its own hand-rolled test/probe setup → don't silently replace it with
    pytest
    . Weigh whether the switch is worth the churn, and if it looks like a real improvement, suggest it to the user instead of doing it unprompted.
  • 编写或修改的每一处逻辑——包括新脚本、新功能或Bug修复——都需要编写测试用例,验证其业务逻辑——即调用方依赖的行为,而非仅验证函数能运行。【绝对禁止违反】绝不能硬编码返回值或断言同义语句来使测试通过——无论是临时测试还是正式测试均需遵守。
  • 若项目已存在测试基础设施(例如
    pytest
    )→ 先编写测试用例,再实现功能。
  • 若项目无测试基础设施,但代码属于现有项目(仓库、包等有后续维护需求的项目)→ 请搭建
    pytest
    ,而非自定义测试运行器。
    pytest
    是事实上的标准,后续任何会话或Agent都知道如何运行它。
  • 若项目无测试基础设施,且代码确实是独立的一次性脚本、无后续项目归属→ 至少编写一个可运行的测试探针:一段简短的独立代码片段,向新代码输入已知数据,并验证实际输出是否符合预期,而非仅验证代码能运行而不崩溃。测试探针符合本规则要求,并非豁免项。
  • 若项目已有自定义的测试/探针机制→ 请勿擅自替换为
    pytest
    。需权衡替换是否值得,若确实是更优方案,请先向用户建议,而非直接修改。

Design Docs

设计文档

  • Design docs, if the project keeps them, go wherever that project's existing documentation convention already puts them. Don't invent a new location (e.g. an ad hoc
    _doc/
    folder) alongside a project that already has one.
  • 若项目有存放设计文档的惯例,请遵循该惯例存放设计文档。请勿在已有存放位置的项目旁新建位置(例如临时的
    _doc/
    文件夹)。