stand-py

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Python Code Standards

Python编码规范

Standards for Python code (>= 3.11).
适用于Python代码(>= 3.11版本)的规范。

Runtime

运行环境

  • All Python code should be run with
    uv
  • 所有Python代码应使用
    uv
    运行

PEP Compliance

PEP合规要求

  • PEP 604: Use union types as
    X | Y
    (not
    Union[X, Y]
    )
  • PEP 673: Use
    Self
    type for self-referential types
  • PEP 604:使用
    X | Y
    作为联合类型(而非
    Union[X, Y]
  • PEP 673:使用
    Self
    类型表示自引用类型

Required Elements

必备元素

  • Type hints on ALL function parameters
  • Return types on ALL functions
  • Docstrings in Google Style Guide format for:
    • Modules
    • Classes
    • Functions/methods
  • 所有函数参数必须添加类型提示
  • 所有函数必须声明返回类型
  • 以下元素需采用Google风格指南格式编写文档字符串:
    • 模块
    • 函数/方法

Docstring Format (Google Style)

文档字符串格式(Google风格)

python
def function_with_docstring(
    param1: str,
    param2: int,
) -> bool:
    """Short description of function.

    Longer description if needed.

    Args:
        param1: Description of param1.
        param2: Description of param2.

    Returns:
        Description of return value.

    Raises:
        ValueError: When something is wrong.
    """
python
def function_with_docstring(
    param1: str,
    param2: int,
) -> bool:
    """函数简短描述。

    如需详细说明可在此处补充。

    参数:
        param1: param1的描述。
        param2: param2的描述。

    返回:
        返回值的描述。

    异常:
        ValueError: 当出现错误时抛出。
    """

Design Patterns

设计模式

  • Use
    dataclasses
    for structured records with fixed fields and named attributes
  • Use
    collections.defaultdict
    for dynamic key-value aggregation with automatic defaults
  • Choose based on the use case: typed record-like object (
    dataclass
    ) vs map with default values (
    defaultdict
    )
  • Each dataclass should be in a separate file
  • String Enums should use
    StrEnum
    with
    auto()
  • Use
    auto()
    with all Enums where it makes sense
  • 使用
    dataclasses
    创建具有固定字段和命名属性的结构化记录
  • 使用
    collections.defaultdict
    实现带自动默认值的动态键值聚合
  • 根据使用场景选择:类型化记录类对象(
    dataclass
    ) vs 带默认值的映射(
    defaultdict
  • 每个dataclass应单独放在一个文件中
  • 字符串枚举应使用带
    auto()
    StrEnum
  • 在合理的情况下,所有枚举都应使用
    auto()

Idioms

编程惯例

Prefer Python-native constructs over verbose cross-language patterns.
  • any()
    /
    all()
    over flag-and-break loops:
    python
    # Don't
    found = False
    for p in paths:
        if p.exists():
            found = True
            break
    
    # Do
    found = any(p.exists() for p in paths)
  • dict.get()
    over key-in checks:
    python
    # Don't
    if name in registry:
        return registry[name]
    return None
    
    # Do
    return registry.get(name)
  • pathlib
    over
    os.path
    — never mix the two in one codebase:
    python
    # Don't
    root = os.path.dirname(os.path.dirname(os.path.dirname(path)))
    
    # Do
    root = Path(path).parents[2]
  • Truthiness over length checks:
    python
    # Don't
    if len(items) == 0:
        ...
    
    # Do
    if not items:
        ...
  • Comprehensions over loop-append for simple transforms:
    python
    # Don't
    names = []
    for user in users:
        names.append(user.name)
    
    # Do
    names = [user.name for user in users]
  • Direct boolean returns:
    python
    # Don't
    if count > limit:
        return True
    return False
    
    # Do
    return count > limit
  • contextlib.suppress(SomeError)
    over a
    try
    /
    except SomeError: pass
    block
  • Reach for
    itertools
    /
    functools
    (
    chain
    ,
    pairwise
    ,
    cache
    ,
    reduce
    ) when they replace hand-rolled loop logic
优先使用Python原生语法,而非冗长的跨语言模式。
  • 使用
    any()
    /
    all()
    替代标志位+中断循环:
    python
    # 不推荐
    found = False
    for p in paths:
        if p.exists():
            found = True
            break
    
    # 推荐
    found = any(p.exists() for p in paths)
  • 使用
    dict.get()
    替代键存在性检查:
    python
    # 不推荐
    if name in registry:
        return registry[name]
    return None
    
    # 推荐
    return registry.get(name)
  • 使用
    pathlib
    替代
    os.path
    — 严禁在同一代码库中混合使用两者:
    python
    # 不推荐
    root = os.path.dirname(os.path.dirname(os.path.dirname(path)))
    
    # 推荐
    root = Path(path).parents[2]
  • 使用真值判断替代长度检查:
    python
    # 不推荐
    if len(items) == 0:
        ...
    
    # 推荐
    if not items:
        ...
  • 使用推导式替代循环+追加操作实现简单转换:
    python
    # 不推荐
    names = []
    for user in users:
        names.append(user.name)
    
    # 推荐
    names = [user.name for user in users]
  • 直接返回布尔值:
    python
    # 不推荐
    if count > limit:
        return True
    return False
    
    # 推荐
    return count > limit
  • 使用
    contextlib.suppress(SomeError)
    替代
    try
    /
    except SomeError: pass
    代码块
  • 当可替代手写循环逻辑时,优先使用
    itertools
    /
    functools
    中的工具(如
    chain
    pairwise
    cache
    reduce

Formatting Rules

格式规则

  • More than 1 arg/param requires a trailing comma:
    python
    # Good
    def foo(bar: str, baz: int,) -> None:
    
    # Bad
    def foo(bar: str, baz: int) -> None:
  • Be explicit with function calls when more than 1 arg:
    python
    # Good
    foo(bar=bar, baz=baz)
    
    # Bad
    foo(bar, baz)
  • Single arg can be positional:
    python
    # OK
    foo(bar)
  • 当参数/实参数量超过1个时,需添加末尾逗号:
    python
    # 规范写法
    def foo(bar: str, baz: int,) -> None:
    
    # 不规范写法
    def foo(bar: str, baz: int) -> None:
  • 当实参数量超过1个时,函数调用需显式指定关键字参数:
    python
    # 规范写法
    foo(bar=bar, baz=baz)
    
    # 不规范写法
    foo(bar, baz)
  • 单个实参可使用位置参数:
    python
    # 允许写法
    foo(bar)

Linting

代码检查

Follow the
lint
skill for linting and formatting workflow.
遵循
lint
技能中的代码检查与格式化流程。

Ignoring Issues

忽略问题规则

Follow the
lint
skill ignore policy (Rules section): fix the root cause first; if suppression is genuinely required, use the narrowest possible ignore (specific rule code, single line) with an inline justification; blanket or file-level ignores require a documented exception.
Python-specific: Bandit requires an inline
# nosec
or
# nosec BXXX - reason
on the same line as the flagged statement (preceding-line
# nosec
is silently ignored by Bandit). See the
lint
skill for other tool ignore configurations (e.g. mypy may use a preceding-line comment plus inline
# type: ignore
):
python
undefined
遵循
lint
技能中的忽略策略(规则部分):优先修复根本问题;若确实需要抑制,使用最窄范围的忽略(特定规则代码、单行)并添加行内说明;全局或文件级忽略需提供文档化的例外说明。
Python特定规则:Bandit要求在标记语句的同一行添加行内
# nosec
# nosec BXXX - reason
(行前的
# nosec
会被Bandit忽略)。其他工具的忽略配置请参考
lint
技能(例如mypy可使用行前注释加行内
# type: ignore
):
python
undefined

Don't - blanket, unjustified ignore

不推荐 - 全局无理由忽略

subprocess.run(["validate.sh"]) # nosec
subprocess.run(["validate.sh"]) # nosec

Do - narrowest code, inline justification

推荐 - 最窄规则代码+行内说明

subprocess.run(["validate.sh"]) # nosec B603 - fixed argv list; no shell

Additional Python-specific note: docstrings are required even for tests — no
exceptions.
subprocess.run(["validate.sh"]) # nosec B603 - 固定argv列表;未使用shell

Python额外注意事项:即使是测试代码也必须编写文档字符串 — 无例外情况。

Testing (Pytest)

测试规范(Pytest)

  • NEVER use unittest style or test classes
  • Use pytest-style test functions only
  • Leverage
    conftest.py
    for shared fixtures
  • Use fixtures for reusable setup/teardown
  • Use
    @pytest.mark.parametrize
    to reduce duplication
  • ALWAYS use
    assertpy
    for assertions — never bare
    assert
    statements. Keep
    pytest.raises
    for exception contexts (assertpy does not replace it)
python
undefined
  • 严禁使用unittest风格或测试类
  • 仅使用pytest风格的测试函数
  • 利用
    conftest.py
    定义共享夹具(fixtures)
  • 使用夹具实现可复用的初始化/清理逻辑
  • 使用
    @pytest.mark.parametrize
    减少代码重复
  • 必须使用
    assertpy
    进行断言 — 绝不使用原生
    assert
    语句。保留
    pytest.raises
    用于异常场景(assertpy无法替代该功能)
python
undefined

Don't

不推荐

assert result.count == 3 assert "drift" in output
assert result.count == 3 assert "drift" in output

Do

推荐

from assertpy import assert_that
assert_that(result.count).is_equal_to(3) assert_that(output).contains("drift")

```python
from assertpy import assert_that
assert_that(result.count).is_equal_to(3) assert_that(output).contains("drift")

```python

WRONG

错误写法

class TestFoo(unittest.TestCase): def test_bar(self): ...
class TestFoo(unittest.TestCase): def test_bar(self): ...

CORRECT

正确写法

def test_foo_bar() -> None: """Verify foo handles bar correctly.""" ...
undefined
def test_foo_bar() -> None: """验证foo正确处理bar场景。""" ...
undefined

Wiring vs. Behavior

关联测试 vs 行为测试

A test that asserts
field == "literal"
where
"literal"
is also defined in source code is duplication, and produces silent drift the moment either side changes.
  • Wiring tests (does X read from canonical Y) — source from the constant. Better still, ask whether the test is just restating the constant's value; if so, delete it. The codegen / source-of-truth machinery is what guarantees that wiring, not a per-consumer assertion.
  • Behavior tests (does X meet a fixed external contract — protocol versions, public API shapes, business rules) — hardcode the literal. The literal is the contract.
  • Fixture data and parser inputs are not wiring — keep those literal. They represent the world being modeled, not internal state.
Parametrize IDs should describe the case under test (
attr=min_version
), never encode mutable data values (
min_version_is_0.43.0
) — IDs that change with every dependency bump are a smell.
若测试断言
field == "literal"
,而
"literal"
同时在源代码中定义,这属于重复代码,且一旦任意一方变更会导致隐性偏差。
  • 关联测试(X是否从标准Y读取数据)—— 从常量中取值。更好的方式是判断该测试是否只是重复常量值;若是,则删除该测试。代码生成/可信源机制可保证关联关系,无需每个消费者都添加断言。
  • 行为测试(X是否符合固定的外部契约——协议版本、公共API结构、业务规则)—— 硬编码字面量。该字面量就是契约本身。
  • 夹具数据和解析器输入不属于关联测试范畴——保留字面量即可。它们代表被建模的外部场景,而非内部状态。
参数化ID应描述测试场景(如
attr=min_version
),绝不能包含可变数据值(如
min_version_is_0.43.0
)—— 每次依赖版本更新都会变更的ID是不良实践。

Parametrize ID Hygiene

参数化ID规范

python
undefined
python
undefined

WRONG — id encodes the data, churns on every bump

错误写法 — ID包含数据,版本更新时会频繁变更

ids=["min_version_is_0.43.0"]
ids=["min_version_is_0.43.0"]

CORRECT — id names the case

正确写法 — ID命名测试场景

ids=["attr=min_version"]
undefined
ids=["attr=min_version"]
undefined