writing-code

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Writing code

代码编写

The house coding conventions. These are personal preferences, not universal law. They win over generic best practice. Where a repository has clearly established a different style, match the repository.
内部编码规范。这些是个人偏好,并非通用准则。它们优先于通用最佳实践。若仓库已明确采用不同风格,则遵循该仓库的风格。

Say so, do not invent

如实说明,勿自行创造

Where a case is not covered below, say that plainly instead of supplying a preference the house has not stated. This holds for a style call, for an audit, and in every language, and it outranks the urge to give a complete-sounding answer.
若以下未涵盖相关情况,请直接说明,而非给出团队未明确的偏好。这适用于风格选择、审核以及所有编程语言,且优先级高于给出看似完整答案的冲动。

Modes

使用场景

A style call while writing code ("how do I name this?", "does this comment earn its place?", "should this be a helper?"). Answer from the matching rule and quote it.
An audit of a change. Check every rule below against the change and report only the rules it actually breaks, as a numbered list: the location, the rule cited by its section and its bold lead-in quoted verbatim, and the concrete edit. Give
file:line
where the change names its file, and the line alone where it does not. Report exactly
No findings.
when nothing is broken. Findings look like this:
1. `labelme/_app.py:1312` (Imports, Imports at the top): move `from pathlib import Path`
   out of the function body.
2. Line 7 (Type annotations, Annotate every signature): add the return type, `-> None`.
编写代码时的风格确认("我该如何命名这个?"、"这个注释是否必要?"、"这应该做成辅助函数吗?")。请根据匹配的规则作答并引用该规则。
变更审核。对照以下每条规则检查变更,仅报告实际违反的规则,以编号列表呈现:位置、引用规则的章节及加粗的标题原文,以及具体的修改建议。若变更指定了文件,给出
file:line
;未指定则仅给出行号。若无违规,需准确报告
No findings.
。违规示例如下:
1. `labelme/_app.py:1312` (Imports, Imports at the top): move `from pathlib import Path`
   out of the function body.
2. Line 7 (Type annotations, Annotate every signature): add the return type, `-> None`.

Outside Python

Python以外的语言

The rules are written in Python because that is the language the house has documented. Most are structural and carry over unchanged: scoping, extraction (minus the
_
prefix below), thin entry points, reading top to bottom, guard clauses, imports, naming, comments, type annotations, and real dependencies over mocks. Apply those in any language, using whatever mechanism it provides.
Some rules are Python spellings of one idea, so carry the idea and drop the spelling.
Final
and
UPPER_CASE
(Scoping and constants): keep the immutability marker, whether
readonly
or
const
, but follow the language's own casing, since Go constants use MixedCaps and ALL_CAPS is not idiomatic there.
xs[:]
(Call sites), the container dunders (Class design), and the pytest structure rules (Tests) translate the same way.
The
_
prefix marking a private helper function has no equivalent worth carrying: it is wrong in Go, where capitalization controls export, and dated in TypeScript. Leave it in Python. Keyword arguments are the opposite case, absent as syntax but alive as a goal, so translate the goal into an options object in TypeScript or named struct fields in Go.
这些规则以Python编写,因为这是团队已记录的语言。大多数规则是结构性的,可直接沿用:作用域、提取(除下文的
_
前缀外)、精简入口点、自上而下阅读、卫语句、导入、命名、注释、类型注解,以及优先使用真实依赖而非模拟依赖。在任何语言中均可应用这些规则,使用该语言对应的机制。
部分规则是某一理念的Python写法,因此应保留理念,舍弃写法。
Final
UPPER_CASE
(作用域与常量):保留不可变性标记,无论是
readonly
还是
const
,但需遵循语言自身的大小写规范,因为Go语言的常量使用MixedCaps,全大写并不符合其惯用写法。
xs[:]
(调用站点)、容器特殊方法(类设计)以及pytest结构规则(测试)同理转换。
标记私有辅助函数的
_
前缀没有值得沿用的等效写法:在Go语言中这是错误的,因为Go通过大小写控制导出;在TypeScript中这已过时。仅在Python中保留该前缀。关键字参数则相反,虽无语法支持,但理念仍适用,因此在TypeScript中可转换为选项对象,在Go中可转换为命名结构体字段。

Scoping and constants

作用域与常量

  • Scope a value to its usage site. A value used in one function lives inside it; promote to module scope only when a second function or module needs it.
  • Parameterize on demand. Keep a single-use value a local constant, and promote it to a parameter when a caller actually varies it.
  • Constants are
    Final
    and
    UPPER_CASE
    .
  • 将值的作用域限定在其使用位置。 仅在一个函数中使用的值应放在该函数内部;仅当第二个函数或模块需要时,才提升到模块作用域。
  • 按需参数化。 仅使用一次的值保持为本地常量,仅当调用方实际需要变更它时,才将其提升为参数。
  • 常量需使用
    Final
    标记并采用
    UPPER_CASE
    命名。

Functions and entry points

函数与入口点

  • Thin entry points.
    main()
    parses arguments and delegates. Lookup tables and config data live inside
    main
    , not at module scope.
  • Extract for scope, not reuse. Extract a single-caller helper to limit a variable's lifetime, narrow its scope, flatten nesting, or when the name adds meaning the expression lacks. Otherwise inline it where it is only one or two lines, since that much indirection buys nothing. Prefix an extracted helper
    _
    unless it is deliberate public API.
  • Read top to bottom. A function that reads straight through beats logic shredded into single-caller helpers, each one a jump the reader must reassemble. A module made mostly of one-caller helpers is fragmented, not modular. A
    # section
    comment inside a long function is the signal to extract; "this could be a function" is not. After any simplification, re-audit the helpers you touched.
  • 精简入口点。
    main()
    负责解析参数并委托执行。查找表和配置数据应放在
    main
    内部,而非模块作用域。
  • 为作用域提取,而非复用。 提取仅被单一调用的辅助函数,以限制变量的生命周期、缩小作用域、减少嵌套,或当函数名称能为表达式补充含义时。否则,若代码仅1-2行则直接内联,因为过多的间接性并无益处。提取的辅助函数需添加
    _
    前缀,除非它是刻意设计的公开API。
  • 自上而下阅读。 逻辑连贯的函数优于被拆分为多个单一调用辅助函数的写法,后者需要读者自行拼接跳转逻辑。一个主要由单一调用辅助函数构成的模块是碎片化的,而非模块化的。长函数内的
    # section
    注释是提取的信号;而“这可以做成函数”则不是。完成任何简化后,需重新审核你修改过的辅助函数。

Control flow

控制流

  • Guard clauses over nesting. Put the happy path at the outer indent and invert the negative cases into early exits, so the real work never hides behind extra indentation.
    if cond: do_a_lot
    becomes
    if not cond: continue
    in a loop, or
    if not cond: return
    in a function.
  • 卫语句优先于嵌套。 将主逻辑放在外层缩进,将异常情况转换为提前退出,避免核心逻辑被额外缩进掩盖。循环中的
    if cond: do_a_lot
    应改为
    if not cond: continue
    ,函数中的则改为
    if not cond: return

Class design

类设计

  • Named edit methods over container dunders.
    shape.points[i]
    and
    shape.move_vertex(i, pos)
    show both the data shape and the mutation surface;
    shape[i] = pos
    hides what is being indexed and conflates the object with its inner collection. Reserve
    __getitem__
    ,
    __setitem__
    ,
    __len__
    , and
    __iter__
    for types whose whole purpose is to be a container, not for domain entities that happen to hold a list.
  • 命名编辑方法优先于容器特殊方法。
    shape.points[i]
    shape.move_vertex(i, pos)
    既展示了数据结构,也明确了修改接口;而
    shape[i] = pos
    则隐藏了索引对象,并将对象与其内部集合混为一谈。仅当类型的核心用途是作为容器时,才使用
    __getitem__
    __setitem__
    __len__
    __iter__
    ,而非用于恰好包含列表的领域实体。

Imports

导入

  • Imports at the top. A deferred import is for breaking a circular dependency, which should be rare.
  • One
    import
    per line.
  • 导入语句放在顶部。 延迟导入仅用于打破循环依赖,这种情况应尽量避免。
  • 每行一个
    import
    语句。

Linting

代码检查

  • Rule selection belongs to the repository. The house pins no ruff or ty rule set here. Read the repository's own linter configuration rather than guessing at it.
  • 规则选择由仓库决定。 团队未在此固定ruff或ty的规则集。请查看仓库自身的检查器配置,而非自行猜测。

Naming and comments

命名与注释

  • Names replace comments. If deleting a docstring makes a function unclear, rename the function.
  • Comment the why. Non-obvious reasoning earns a comment; restating what the code does does not.
  • Keep identifiers out of comment prose. A comment that names another function goes stale the moment that function is renamed, and nothing catches it.
  • Verb-prefixed function names. Start a function name with a verb naming what it does (
    make_local_mask
    ,
    compute_mask_iou
    ,
    round_bbox_to_int
    ). Use a non-verb form only where it reads strictly better: predicates (
    is_*
    ,
    has_*
    ,
    can_*
    ,
    should_*
    ), classmethod constructors (
    from_*
    ), and conversion idioms (
    to_dict
    ). Prefer singular
    is_*
    over
    are_*
    , naming the subject:
    is_redundant_pair(new, peer)
    , not
    are_redundant(new, peer)
    . A noun-only name (
    mask_iou
    ) or an adjective-noun name (
    filled_mask_for_bbox
    ) reads as a value, not an action.
  • 名称替代注释。 若删除文档字符串会导致函数含义模糊,请重命名该函数。
  • 注释说明原因。 非显而易见的逻辑需要注释;重复代码功能的注释并无必要。
  • 避免在注释中提及标识符。 提及其他函数的注释会在该函数重命名后失效,且无机制能检测到这种失效。
  • 函数名称以动词开头。 函数名称需以动词开头,表明其功能(
    make_local_mask
    compute_mask_iou
    round_bbox_to_int
    )。仅在可读性明显更好时使用非动词形式:谓词(
    is_*
    has_*
    can_*
    should_*
    )、类方法构造器(
    from_*
    )以及转换惯用写法(
    to_dict
    )。优先使用单数形式的
    is_*
    而非
    are_*
    ,并明确主语:
    is_redundant_pair(new, peer)
    ,而非
    are_redundant(new, peer)
    。仅含名词的名称(
    mask_iou
    )或形容词+名词的名称(
    filled_mask_for_bbox
    )会被视为值,而非动作。

Call sites

调用站点

  • Spell out keyword arguments, unless the call is trivially obvious (
    len(x)
    ,
    max(items)
    ,
    shape(aoi)
    ).
  • Copy a list with
    xs[:]
    ,
    which reads as "copy this list", where the source is statically a
    list
    . Reserve
    list(...)
    for converting a genuine non-list iterable, or where
    list(xs or [])
    beats
    (xs or [])[:]
    .
  • 明确写出关键字参数, 除非调用极其明显(
    len(x)
    max(items)
    shape(aoi)
    )。
  • 使用
    xs[:]
    复制列表,
    当源是静态
    list
    时,该写法表示“复制此列表”。仅当转换真正的非列表可迭代对象,或
    list(xs or [])
    (xs or [])[:]
    更简洁时,才使用
    list(...)

Type annotations

类型注解

  • Annotate every signature, parameters and return type, including
    -> None
    on tests.
  • Leave inferable locals bare. Annotate only the ones the type checker cannot infer:
    results: list[Hunk] = []
    ,
    exclude: bool | None = None
    . A
    Final
    marker on a fixed value is not a local annotation in this sense.
  • Migration exception. Annotate locals while upstream functions lack annotations, and remove those once upstream is fixed.
  • 为所有签名添加注解, 包括参数和返回类型,测试函数也需添加
    -> None
  • 可推导的本地变量无需注解。 仅为类型检查器无法推导的变量添加注解:
    results: list[Hunk] = []
    exclude: bool | None = None
    。固定值上的
    Final
    标记不属于此类本地注解。
  • 迁移例外。 当上游函数缺少注解时,可为本地变量添加注解,待上游修复后再移除这些注解。

Tests

测试

  • Plain
    test_
    functions with fixtures,
    never test classes.
  • Mirror the source layout. Test directories mirror source modules, and tests live in them rather than beside the code they cover. A module with several test files gets a subdirectory named after it (
    tests/unit/hunk/
    for
    hunk.py
    ), and each file is named for the aspect it covers (
    id_test.py
    , not
    hunk_id_test.py
    ).
  • Split test files instead of separating groups with comments.
  • Deduplicate shared setup into a
    @pytest.fixture
    .
  • Real dependencies over mocks. Drive a test through the real thing wherever it is cheap: an existing conftest fixture, an ephemeral subprocess, a dockerized service, an in-memory engine. Mocking a downstream system verifies only your reading of its API, and keeps passing after the real API changes shape. Reserve mocks for paid third-party APIs, irreversible side effects such as payments or mail to humans, and services with no offline mode. When unsure, measure: a sub-second real-dependency test beats the equivalent
    MagicMock
    .
  • 使用带fixture的普通
    test_
    函数,
    绝不使用测试类。
  • 镜像源码结构。 测试目录需镜像源码模块,测试文件放在对应的目录中,而非被测试代码的旁边。若一个模块有多个测试文件,需创建以该模块命名的子目录(如
    hunk.py
    对应
    tests/unit/hunk/
    ),每个文件以其测试的方面命名(
    id_test.py
    ,而非
    hunk_id_test.py
    )。
  • 拆分测试文件,而非用注释分隔测试组。
  • 将共享的初始化逻辑抽象为
    @pytest.fixture
  • 优先使用真实依赖而非模拟依赖。 只要成本可控,就通过真实组件驱动测试:现有conftest fixture、临时子进程、容器化服务、内存引擎。模拟下游系统仅能验证你对其API的理解,且在真实API变更后仍可能通过测试。仅在以下场景使用模拟:付费第三方API、不可逆转的副作用(如支付或向用户发送邮件)、无离线模式的服务。若不确定,可测试耗时:耗时亚秒级的真实依赖测试优于等效的
    MagicMock
    测试。